Update Underscore/Backbone tests and make them passable.

This commit is contained in:
John-David Dalton
2015-08-08 16:52:47 -07:00
parent de5c2b906e
commit bd9b38665d
13 changed files with 843 additions and 550 deletions

View File

@@ -977,16 +977,19 @@
// normal circumstances, as the set will maintain sort order as each item
// is added.
sort: function(options) {
if (!this.comparator) throw new Error('Cannot sort a set without a comparator');
var comparator = this.comparator;
if (!comparator) throw new Error('Cannot sort a set without a comparator');
options || (options = {});
// Run sort based on type of `comparator`.
if (_.isString(this.comparator) || this.comparator.length === 1) {
this.models = this.sortBy(this.comparator, this);
} else {
this.models.sort(_.bind(this.comparator, this));
}
var length = comparator.length;
if (_.isFunction(comparator)) comparator = _.bind(comparator, this);
// Run sort based on type of `comparator`.
if (length === 1 || _.isString(comparator)) {
this.models = this.sortBy(comparator);
} else {
this.models.sort(comparator);
}
if (!options.silent) this.trigger('sort', this, options);
return this;
},
@@ -1144,8 +1147,8 @@
// right here:
var collectionMethods = { forEach: 3, each: 3, map: 3, collect: 3, reduce: 4,
foldl: 4, inject: 4, reduceRight: 4, foldr: 4, find: 3, detect: 3, filter: 3,
select: 3, reject: 3, every: 3, all: 3, some: 3, any: 3, include: 2,
contains: 2, invoke: 0, max: 3, min: 3, toArray: 1, size: 1, first: 3,
select: 3, reject: 3, every: 3, all: 3, some: 3, any: 3, include: 3, includes: 3,
contains: 3, invoke: 0, max: 3, min: 3, toArray: 1, size: 1, first: 3,
head: 3, take: 3, initial: 3, rest: 3, tail: 3, drop: 3, last: 3,
without: 0, difference: 0, indexOf: 3, shuffle: 1, lastIndexOf: 3,
isEmpty: 1, chain: 1, sample: 3, partition: 3, groupBy: 3, countBy: 3,

View File

@@ -625,24 +625,26 @@
test("Underscore methods", 19, function() {
equal(col.map(function(model){ return model.get('label'); }).join(' '), 'a b c d');
equal(col.any(function(model){ return model.id === 100; }), false);
equal(col.any(function(model){ return model.id === 0; }), true);
equal(col.some(function(model){ return model.id === 100; }), false);
equal(col.some(function(model){ return model.id === 0; }), true);
equal(col.indexOf(b), 1);
equal(col.size(), 4);
equal(col.rest().length, 3);
ok(!_.include(col.rest(), a));
ok(_.include(col.rest(), d));
ok(!_.includes(col.rest(), a));
ok(_.includes(col.rest(), d));
ok(!col.isEmpty());
ok(!_.include(col.without(d), d));
equal(col.max(function(model){ return model.id; }).id, 3);
equal(col.min(function(model){ return model.id; }).id, 0);
deepEqual(col.chain()
ok(!_.includes(col.without(d), d));
var wrapped = col.chain();
equal(wrapped.map('id').max().value(), 3);
equal(wrapped.map('id').min().value(), 0);
deepEqual(wrapped
.filter(function(o){ return o.id % 2 === 0; })
.map(function(o){ return o.id * 2; })
.value(),
[4, 0]);
[4, 0]);
deepEqual(col.difference([c, d]), [a, b]);
ok(col.include(col.sample()));
ok(col.includes(col.sample()));
var first = col.first();
deepEqual(col.groupBy(function(model){ return model.id; })[first.id], [first]);
deepEqual(col.countBy(function(model){ return model.id; }), {0: 1, 1: 1, 2: 1, 3: 1});
@@ -676,8 +678,8 @@
deepEqual(coll.partition({a: 4})[1], _.without(coll.models, model));
deepEqual(coll.map({a: 2}), [false, true, false, false]);
deepEqual(coll.map('a'), [1, 2, 3, 4]);
deepEqual(coll.max('a'), model);
deepEqual(coll.min('e'), model);
deepEqual(coll.sortBy('a')[3], model);
deepEqual(coll.sortBy('e')[0], model);
deepEqual(coll.countBy({a: 4}), {'false': 3, 'true': 1});
deepEqual(coll.countBy('d'), {'undefined': 4});
});
@@ -1175,7 +1177,7 @@
var Model = Backbone.Model.extend({});
var Collection = Backbone.Collection.extend({
model: Model,
parse: function (res) { return _.pluck(res.models, 'model'); }
parse: function (res) { return _.map(res.models, 'model'); }
});
var model = new Model({id: 1});
var collection = new Collection(model);

View File

@@ -1,6 +1,4 @@
$('body').append(
'<div id="qunit"></div>' +
'<div id="qunit-fixture">' +
'<div id="testElement"><h1>Test</h1></div>' +
'</div>'
);
'<div id="qunit-fixture"></div>'
);

View File

@@ -5,6 +5,10 @@
module("Backbone.View", {
setup: function() {
$('#qunit-fixture').append(
'<div id="testElement"><h1>Test</h1></div>'
);
view = new Backbone.View({
id : 'test-view',
className : 'test-view',

View File

@@ -16,7 +16,7 @@
result = (function() { return _.first([1, 2, 3], 2); }());
deepEqual(result, [1, 2]);
equal(_.first(null), undefined, 'handles nulls');
equal(_.first(null), void 0, 'handles nulls');
strictEqual(_.first([1, 2, 3], -1).length, 0);
});
@@ -69,7 +69,7 @@
result = _.map([[1, 2, 3], [1, 2, 3]], _.last);
deepEqual(result, [3, 3], 'works well with _.map');
equal(_.last(null), undefined, 'handles nulls');
equal(_.last(null), void 0, 'handles nulls');
strictEqual(_.last([1, 2, 3], -1).length, 0);
});
@@ -98,6 +98,11 @@
equal(_.flatten([_.range(10), _.range(10), 5, 1, 3]).length, 23);
equal(_.flatten([new Array(1000000), _.range(56000), 5, 1, 3]).length, 1056003, 'Flatten can handle massive collections');
equal(_.flatten([new Array(1000000), _.range(56000), 5, 1, 3], true).length, 1056003, 'Flatten can handle massive collections');
var x = _.range(100000);
for (var i = 0; i < 1000; i++) x = [x];
deepEqual(_.flatten(x), _.range(100000), 'Flatten can handle very deep arrays');
deepEqual(_.flatten(x, true), x[0], 'Flatten can handle very deep arrays with shallow');
});
test('without', function() {
@@ -106,8 +111,8 @@
var result = (function(){ return _.without(arguments, 0, 1); }(1, 2, 1, 0, 3, 1, 4));
deepEqual(result, [2, 3, 4], 'works on an arguments object');
list = [{one : 1}, {two : 2}];
equal(_.without(list, {one : 1}).length, 2, 'uses real object identity for comparisons.');
list = [{one: 1}, {two: 2}];
equal(_.without(list, {one: 1}).length, 2, 'uses real object identity for comparisons.');
equal(_.without(list, list[0]).length, 1, 'ditto.');
});
@@ -242,8 +247,8 @@
var names = ['moe', 'larry', 'curly'], ages = [30, 40, 50], leaders = [true];
deepEqual(_.zip(names, ages, leaders), [
['moe', 30, true],
['larry', 40, undefined],
['curly', 50, undefined]
['larry', 40, void 0],
['curly', 50, void 0]
], 'zipped together arrays of different lengths');
var stooges = _.zip(['moe', 30, 'stooge 1'], ['larry', 40, 'stooge 2'], ['curly', 50, 'stooge 3']);
@@ -252,7 +257,7 @@
// In the case of difference lengths of the tuples undefineds
// should be used as placeholder
stooges = _.zip(['moe', 30], ['larry', 40], ['curly', 50, 'extra data']);
deepEqual(stooges, [['moe', 'larry', 'curly'], [30, 40, 50], [undefined, undefined, 'extra data']], 'zipped pairs with empties');
deepEqual(stooges, [['moe', 'larry', 'curly'], [30, 40, 50], [void 0, void 0, 'extra data']], 'zipped pairs with empties');
var empty = _.zip([]);
deepEqual(empty, [], 'unzipped empty');
@@ -324,7 +329,7 @@
index = _.indexOf(numbers, 2, 5);
equal(index, 7, 'supports the fromIndex argument');
index = _.indexOf([,,,], undefined);
index = _.indexOf([,,, 0], void 0);
equal(index, 0, 'treats sparse arrays as if they were dense');
var array = [1, 2, 3, 1, 2, 3];
@@ -336,7 +341,7 @@
});
strictEqual(_.indexOf([1, 2, 3], 1, true), 0);
index = _.indexOf([], undefined, true);
index = _.indexOf([], void 0, true);
equal(index, -1, 'empty array with truthy `isSorted` returns -1');
});
@@ -361,7 +366,7 @@
test('lastIndexOf', function() {
var numbers = [1, 0, 1];
var falsey = [void 0, '', 0, false, NaN, null, undefined];
var falsey = [void 0, '', 0, false, NaN, null, void 0];
equal(_.lastIndexOf(numbers, 1), 2);
numbers = [1, 0, 1, 0, 0, 1, 0, 0, 0];
@@ -392,7 +397,7 @@
strictEqual(_.lastIndexOf(array, 1, 2), 0, 'should work with a positive `fromIndex`');
_.each([6, 8, Math.pow(2, 32), Infinity], function(fromIndex) {
strictEqual(_.lastIndexOf(array, undefined, fromIndex), -1);
strictEqual(_.lastIndexOf(array, void 0, fromIndex), -1);
strictEqual(_.lastIndexOf(array, 1, fromIndex), 3);
strictEqual(_.lastIndexOf(array, '', fromIndex), -1);
});
@@ -439,10 +444,10 @@
test('findIndex', function() {
var objects = [
{'a': 0, 'b': 0},
{'a': 1, 'b': 1},
{'a': 2, 'b': 2},
{'a': 0, 'b': 0}
{a: 0, b: 0},
{a: 1, b: 1},
{a: 2, b: 2},
{a: 0, b: 0}
];
equal(_.findIndex(objects, function(obj) {
@@ -470,7 +475,7 @@
}, objects);
var sparse = [];
sparse[20] = {'a': 2, 'b': 2};
sparse[20] = {a: 2, b: 2};
equal(_.findIndex(sparse, function(obj) {
return obj && obj.b * obj.a === 4;
}), 20, 'Works with sparse arrays');
@@ -482,10 +487,10 @@
test('findLastIndex', function() {
var objects = [
{'a': 0, 'b': 0},
{'a': 1, 'b': 1},
{'a': 2, 'b': 2},
{'a': 0, 'b': 0}
{a: 0, b: 0},
{a: 1, b: 1},
{a: 2, b: 2},
{a: 0, b: 0}
];
equal(_.findLastIndex(objects, function(obj) {
@@ -513,7 +518,7 @@
}, objects);
var sparse = [];
sparse[20] = {'a': 2, 'b': 2};
sparse[20] = {a: 2, b: 2};
equal(_.findLastIndex(sparse, function(obj) {
return obj && obj.b * obj.a === 4;
}), 20, 'Works with sparse arrays');
@@ -534,4 +539,19 @@
deepEqual(_.range(0, -10, -1), [0, -1, -2, -3, -4, -5, -6, -7, -8, -9], 'final example in the Python docs');
});
test('chunk', function() {
deepEqual(_.chunk([], 2), [], 'chunk for empty array returns an empty array');
deepEqual(_.chunk([1, 2, 3], 0), [], 'chunk into parts of 0 elements returns empty array');
deepEqual(_.chunk([1, 2, 3], -1), [], 'chunk into parts of negative amount of elements returns an empty array');
deepEqual(_.chunk([1, 2, 3]), [], 'defaults to empty array (chunk size 0)');
deepEqual(_.chunk([1, 2, 3], 1), [[1], [2], [3]], 'chunk into parts of 1 elements returns original array');
deepEqual(_.chunk([1, 2, 3], 3), [[1, 2, 3]], 'chunk into parts of current array length elements returns the original array');
deepEqual(_.chunk([1, 2, 3], 5), [[1, 2, 3]], 'chunk into parts of more then current array length elements returns the original array');
deepEqual(_.chunk([10, 20, 30, 40, 50, 60, 70], 2), [[10, 20], [30, 40], [50, 60], [70]], 'chunk into parts of less then current array length elements');
deepEqual(_.chunk([10, 20, 30, 40, 50, 60, 70], 3), [[10, 20, 30], [40, 50, 60], [70]], 'chunk into parts of less then current array length elements');
});
}());

View File

@@ -17,7 +17,8 @@
hash[l] = hash[l] || 0;
hash[l]++;
return hash;
}, {}).value();
}, {})
.value();
equal(counts.a, 16, 'counted all the letters in the song');
equal(counts.e, 10, 'counted all the letters in the song');
});

View File

@@ -9,7 +9,7 @@
});
var answers = [];
_.each([1, 2, 3], function(num){ answers.push(num * this.multiplier);}, {multiplier : 5});
_.each([1, 2, 3], function(num){ answers.push(num * this.multiplier); }, {multiplier: 5});
deepEqual(answers, [5, 10, 15], 'context object property accessed');
answers = [];
@@ -17,7 +17,7 @@
deepEqual(answers, [1, 2, 3], 'aliased as "forEach"');
answers = [];
var obj = {one : 1, two : 2, three : 3};
var obj = {one: 1, two: 2, three: 3};
obj.constructor.prototype.four = 4;
_.each(obj, function(value, key){ answers.push(key); });
deepEqual(answers, ['one', 'two', 'three'], 'iterating over objects works, and ignores the object prototype.');
@@ -26,8 +26,8 @@
// ensure the each function is JITed
_(1000).times(function() { _.each([], function(){}); });
var count = 0;
obj = {1 : 'foo', 2 : 'bar', 3 : 'baz'};
_.each(obj, function(value, key){ count++; });
obj = {1: 'foo', 2: 'bar', 3: 'baz'};
_.each(obj, function(){ count++; });
equal(count, 3, 'the fun should be called only 3 times');
var answer = null;
@@ -149,7 +149,7 @@
var doubled = _.map([1, 2, 3], function(num){ return num * 2; });
deepEqual(doubled, [2, 4, 6], 'doubled numbers');
var tripled = _.map([1, 2, 3], function(num){ return num * this.multiplier; }, {multiplier : 3});
var tripled = _.map([1, 2, 3], function(num){ return num * this.multiplier; }, {multiplier: 3});
deepEqual(tripled, [3, 6, 9], 'tripled numbers with context');
doubled = _([1, 2, 3]).map(function(num){ return num * 2; });
@@ -167,7 +167,7 @@
}, [5]), [1], 'called with context');
// Passing a property name like _.pluck.
var people = [{name : 'moe', age : 30}, {name : 'curly', age : 50}];
var people = [{name: 'moe', age: 30}, {name: 'curly', age: 50}];
deepEqual(_.map(people, 'name'), ['moe', 'curly'], 'predicate string map to object properties');
});
@@ -176,29 +176,29 @@
});
test('reduce', function() {
var sum = _.reduce([1, 2, 3], function(sum, num){ return sum + num; }, 0);
var sum = _.reduce([1, 2, 3], function(memo, num){ return memo + num; }, 0);
equal(sum, 6, 'can sum up an array');
var context = {multiplier : 3};
sum = _.reduce([1, 2, 3], function(sum, num){ return sum + num * this.multiplier; }, 0, context);
var context = {multiplier: 3};
sum = _.reduce([1, 2, 3], function(memo, num){ return memo + num * this.multiplier; }, 0, context);
equal(sum, 18, 'can reduce with a context object');
sum = _.inject([1, 2, 3], function(sum, num){ return sum + num; }, 0);
sum = _.inject([1, 2, 3], function(memo, num){ return memo + num; }, 0);
equal(sum, 6, 'aliased as "inject"');
sum = _([1, 2, 3]).reduce(function(sum, num){ return sum + num; }, 0);
sum = _([1, 2, 3]).reduce(function(memo, num){ return memo + num; }, 0);
equal(sum, 6, 'OO-style reduce');
sum = _.reduce([1, 2, 3], function(sum, num){ return sum + num; });
sum = _.reduce([1, 2, 3], function(memo, num){ return memo + num; });
equal(sum, 6, 'default initial value');
var prod = _.reduce([1, 2, 3, 4], function(prod, num){ return prod * num; });
var prod = _.reduce([1, 2, 3, 4], function(memo, num){ return memo * num; });
equal(prod, 24, 'can reduce via multiplication');
ok(_.reduce(null, _.noop, 138) === 138, 'handles a null (with initial value) properly');
equal(_.reduce([], _.noop, undefined), undefined, 'undefined can be passed as a special case');
equal(_.reduce([], _.noop, void 0), void 0, 'undefined can be passed as a special case');
equal(_.reduce([_], _.noop), _, 'collection of length one with no initial value returns the first item');
equal(_.reduce([], _.noop), undefined, 'returns undefined when collection is empty and no initial value');
equal(_.reduce([], _.noop), void 0, 'returns undefined when collection is empty and no initial value');
});
test('foldl', function() {
@@ -212,45 +212,45 @@
list = _.reduceRight(['foo', 'bar', 'baz'], function(memo, str){ return memo + str; });
equal(list, 'bazbarfoo', 'default initial value');
var sum = _.reduceRight({a: 1, b: 2, c: 3}, function(sum, num){ return sum + num; });
var sum = _.reduceRight({a: 1, b: 2, c: 3}, function(memo, num){ return memo + num; });
equal(sum, 6, 'default initial value on object');
ok(_.reduceRight(null, _.noop, 138) === 138, 'handles a null (with initial value) properly');
equal(_.reduceRight([_], _.noop), _, 'collection of length one with no initial value returns the first item');
equal(_.reduceRight([], _.noop, undefined), undefined, 'undefined can be passed as a special case');
equal(_.reduceRight([], _.noop), undefined, 'returns undefined when collection is empty and no initial value');
equal(_.reduceRight([], _.noop, void 0), void 0, 'undefined can be passed as a special case');
equal(_.reduceRight([], _.noop), void 0, 'returns undefined when collection is empty and no initial value');
// Assert that the correct arguments are being passed.
var args,
memo = {},
init = {},
object = {a: 1, b: 2},
lastKey = _.keys(object).pop();
var expected = lastKey === 'a'
? [memo, 1, 'a', object]
: [memo, 2, 'b', object];
? [init, 1, 'a', object]
: [init, 2, 'b', object];
_.reduceRight(object, function() {
if (!args) args = _.toArray(arguments);
}, memo);
}, init);
deepEqual(args, expected);
// And again, with numeric keys.
object = {'2': 'a', '1': 'b'};
object = {2: 'a', 1: 'b'};
lastKey = _.keys(object).pop();
args = null;
expected = lastKey === '2'
? [memo, 'a', '2', object]
: [memo, 'b', '1', object];
? [init, 'a', '2', object]
: [init, 'b', '1', object];
_.reduceRight(object, function() {
if (!args) args = _.toArray(arguments);
}, memo);
}, init);
deepEqual(args, expected);
});
@@ -290,9 +290,9 @@
return x.x === 4;
}), {x: 4, z: 1});
_.findIndex([{a: 1}], function(a, key, obj) {
_.findIndex([{a: 1}], function(a, key, o) {
equal(key, 0);
deepEqual(obj, [{a: 1}]);
deepEqual(o, [{a: 1}]);
strictEqual(this, _, 'called with context');
}, _);
});
@@ -356,7 +356,7 @@
ok(!_.every([0, 11, 28], function(num){ return num % 2 === 0; }), 'an odd number');
ok(_.every([1], _.identity) === true, 'cast to boolean - true');
ok(_.every([0], _.identity) === false, 'cast to boolean - false');
ok(!_.every([undefined, undefined, undefined], _.identity), 'works with arrays of undefined');
ok(!_.every([void 0, void 0, void 0], _.identity), 'works with arrays of undefined');
var list = [{a: 1, b: 2}, {a: 2, b: 2}, {a: 1, b: 3}, {a: 1, b: 4}];
ok(!_.every(list, {a: 1, b: 2}), 'Can be called with object');
@@ -493,13 +493,13 @@
deepEqual(result[0], [1, 5, 7], 'first array sorted');
deepEqual(result[1], [1, 2, 3], 'second array sorted');
delete String.prototype.call;
equal(s.call, undefined, 'call function removed');
equal(s.call, void 0, 'call function removed');
});
test('pluck', function() {
var people = [{name: 'moe', age: 30}, {name: 'curly', age: 50}];
deepEqual(_.pluck(people, 'name'), ['moe', 'curly'], 'pulls names out of objects');
deepEqual(_.pluck(people, 'address'), [undefined, undefined], 'missing properties are returned as undefined');
deepEqual(_.pluck(people, 'address'), [void 0, void 0], 'missing properties are returned as undefined');
//compat: most flexible handling of edge cases
deepEqual(_.pluck([{'[object Object]': 1}], {}), [1]);
});
@@ -547,7 +547,7 @@
test('max', function() {
equal(-Infinity, _.max(null), 'can handle null/undefined');
equal(-Infinity, _.max(undefined), 'can handle null/undefined');
equal(-Infinity, _.max(void 0), 'can handle null/undefined');
equal(-Infinity, _.max(null, _.identity), 'can handle null/undefined');
equal(3, _.max([1, 2, 3]), 'can perform a regular Math.max');
@@ -557,28 +557,30 @@
equal(-Infinity, _.max({}), 'Maximum value of an empty object');
equal(-Infinity, _.max([]), 'Maximum value of an empty array');
equal(_.max({'a': 'a'}), -Infinity, 'Maximum value of a non-numeric collection');
equal(_.max({a: 'a'}), -Infinity, 'Maximum value of a non-numeric collection');
equal(299999, _.max(_.range(1, 300000)), 'Maximum value of a too-big array');
equal(3, _.max([1, 2, 3, 'test']), 'Finds correct max in array starting with num and containing a NaN');
equal(3, _.max(['test', 1, 2, 3]), 'Finds correct max in array starting with NaN');
deepEqual([3, 6], _.map([[1, 2, 3], [4, 5, 6]], _.max), 'Finds correct max in array when mapping through multiple arrays');
var a = {x: -Infinity};
var b = {x: -Infinity};
var iterator = function(o){ return o.x; };
equal(_.max([a, b], iterator), a, 'Respects iterator return value of -Infinity');
deepEqual(_.max([{'a': 1}, {'a': 0, 'b': 3}, {'a': 4}, {'a': 2}], 'a'), {'a': 4}, 'String keys use property iterator');
deepEqual(_.max([{a: 1}, {a: 0, b: 3}, {a: 4}, {a: 2}], 'a'), {a: 4}, 'String keys use property iterator');
deepEqual(_.max([0, 2], function(a){ return a * this.x; }, {x: 1}), 2, 'Iterator context');
deepEqual(_.max([0, 2], function(c){ return c * this.x; }, {x: 1}), 2, 'Iterator context');
deepEqual(_.max([[1], [2, 3], [-1, 4], [5]], 0), [5], 'Lookup falsy iterator');
deepEqual(_.max([{0: 1}, {0: 2}, {0: -1}, {a: 1}], 0), {0: 2}, 'Lookup falsy iterator');
});
test('min', function() {
equal(Infinity, _.min(null), 'can handle null/undefined');
equal(Infinity, _.min(undefined), 'can handle null/undefined');
equal(Infinity, _.min(void 0), 'can handle null/undefined');
equal(Infinity, _.min(null, _.identity), 'can handle null/undefined');
equal(1, _.min([1, 2, 3]), 'can perform a regular Math.min');
@@ -588,7 +590,9 @@
equal(Infinity, _.min({}), 'Minimum value of an empty object');
equal(Infinity, _.min([]), 'Minimum value of an empty array');
equal(_.min({'a': 'a'}), Infinity, 'Minimum value of a non-numeric collection');
equal(_.min({a: 'a'}), Infinity, 'Minimum value of a non-numeric collection');
deepEqual([1, 4], _.map([[1, 2, 3], [4, 5, 6]], _.min), 'Finds correct min in array when mapping through multiple arrays');
var now = new Date(9999999999);
var then = new Date(0);
@@ -604,20 +608,20 @@
var iterator = function(o){ return o.x; };
equal(_.min([a, b], iterator), a, 'Respects iterator return value of Infinity');
deepEqual(_.min([{'a': 1}, {'a': 0, 'b': 3}, {'a': 4}, {'a': 2}], 'a'), {'a': 0, 'b': 3}, 'String keys use property iterator');
deepEqual(_.min([{a: 1}, {a: 0, b: 3}, {a: 4}, {a: 2}], 'a'), {a: 0, b: 3}, 'String keys use property iterator');
deepEqual(_.min([0, 2], function(a){ return a * this.x; }, {x: -1}), 2, 'Iterator context');
deepEqual(_.min([0, 2], function(c){ return c * this.x; }, {x: -1}), 2, 'Iterator context');
deepEqual(_.min([[1], [2, 3], [-1, 4], [5]], 0), [-1, 4], 'Lookup falsy iterator');
deepEqual(_.min([{0: 1}, {0: 2}, {0: -1}, {a: 1}], 0), {0: -1}, 'Lookup falsy iterator');
});
test('sortBy', function() {
var people = [{name : 'curly', age : 50}, {name : 'moe', age : 30}];
var people = [{name: 'curly', age: 50}, {name: 'moe', age: 30}];
people = _.sortBy(people, function(person){ return person.age; });
deepEqual(_.pluck(people, 'name'), ['moe', 'curly'], 'stooges sorted by age');
var list = [undefined, 4, 1, undefined, 3, 2];
deepEqual(_.sortBy(list, _.identity), [1, 2, 3, 4, undefined, undefined], 'sortBy with undefined values');
var list = [void 0, 4, 1, void 0, 3, 2];
deepEqual(_.sortBy(list, _.identity), [1, 2, 3, 4, void 0, void 0], 'sortBy with undefined values');
list = ['one', 'two', 'three', 'four', 'five'];
var sorted = _.sortBy(list, 'length');
@@ -628,25 +632,32 @@
this.y = y;
}
var collection = [
var stableArray = [
new Pair(1, 1), new Pair(1, 2),
new Pair(1, 3), new Pair(1, 4),
new Pair(1, 5), new Pair(1, 6),
new Pair(2, 1), new Pair(2, 2),
new Pair(2, 3), new Pair(2, 4),
new Pair(2, 5), new Pair(2, 6),
new Pair(undefined, 1), new Pair(undefined, 2),
new Pair(undefined, 3), new Pair(undefined, 4),
new Pair(undefined, 5), new Pair(undefined, 6)
new Pair(void 0, 1), new Pair(void 0, 2),
new Pair(void 0, 3), new Pair(void 0, 4),
new Pair(void 0, 5), new Pair(void 0, 6)
];
var actual = _.sortBy(collection, function(pair) {
var stableObject = _.object('abcdefghijklmnopqr'.split(''), stableArray);
var actual = _.sortBy(stableArray, function(pair) {
return pair.x;
});
deepEqual(actual, collection, 'sortBy should be stable');
deepEqual(actual, stableArray, 'sortBy should be stable for arrays');
deepEqual(_.sortBy(stableArray, 'x'), stableArray, 'sortBy accepts property string');
deepEqual(_.sortBy(collection, 'x'), collection, 'sortBy accepts property string');
actual = _.sortBy(stableObject, function(pair) {
return pair.x;
});
deepEqual(actual, stableArray, 'sortBy should be stable for objects');
list = ['q', 'w', 'e', 'r', 't', 'y'];
deepEqual(_.sortBy(list), ['e', 'q', 'r', 't', 'w', 'y'], 'uses _.identity if iterator is not specified');
@@ -737,12 +748,12 @@
});
test('shuffle', function() {
var numbers = _.range(10);
deepEqual(_.shuffle([1]), [1], 'behaves correctly on size 1 arrays');
var numbers = _.range(20);
var shuffled = _.shuffle(numbers);
notDeepEqual(numbers, shuffled, 'does change the order'); // Chance of false negative: 1 in ~2.4*10^18
notStrictEqual(numbers, shuffled, 'original object is unmodified');
ok(_.every(_.range(10), function() { //appears consistent?
return _.every(numbers, _.partial(_.contains, numbers));
}), 'contains the same members before and after shuffle');
deepEqual(numbers, _.sortBy(shuffled), 'contains the same members before and after shuffle');
shuffled = _.shuffle({a: 1, b: 2, c: 3, d: 4});
equal(shuffled.length, 4);
@@ -750,17 +761,22 @@
});
test('sample', function() {
strictEqual(_.sample([1]), 1, 'behaves correctly when no second parameter is given');
deepEqual(_.sample([1, 2, 3], -2), [], 'behaves correctly on negative n');
var numbers = _.range(10);
var allSampled = _.sample(numbers, 10).sort();
deepEqual(allSampled, numbers, 'contains the same members before and after sample');
allSampled = _.sample(numbers, 20).sort();
deepEqual(allSampled, numbers, 'also works when sampling more objects than are present');
ok(_.contains(numbers, _.sample(numbers)), 'sampling a single element returns something from the array');
strictEqual(_.sample([]), undefined, 'sampling empty array with no number returns undefined');
strictEqual(_.sample([]), void 0, 'sampling empty array with no number returns undefined');
notStrictEqual(_.sample([], 5), [], 'sampling empty array with a number returns an empty array');
notStrictEqual(_.sample([1, 2, 3], 0), [], 'sampling an array with 0 picks returns an empty array');
deepEqual(_.sample([1, 2], -1), [], 'sampling a negative number of picks returns an empty array');
ok(_.contains([1, 2, 3], _.sample({a: 1, b: 2, c: 3})), 'sample one value from an object');
var partialSample = _.sample(_.range(1000), 10);
var partialSampleSorted = partialSample.sort();
notDeepEqual(partialSampleSorted, _.range(10), 'samples from the whole array, not just the beginning');
});
test('toArray', function() {
@@ -770,7 +786,7 @@
ok(_.toArray(a) !== a, 'array is cloned');
deepEqual(_.toArray(a), [1, 2, 3], 'cloned array contains same elements');
var numbers = _.toArray({one : 1, two : 2, three : 3});
var numbers = _.toArray({one: 1, two: 2, three: 3});
deepEqual(numbers, [1, 2, 3], 'object flattened into array');
if (typeof document != 'undefined') {
@@ -778,13 +794,13 @@
var actual;
try {
actual = _.toArray(document.childNodes);
} catch(ex) { }
} catch(e) { /* ignored */ }
deepEqual(actual, _.map(document.childNodes, _.identity), 'works on NodeList');
}
});
test('size', function() {
equal(_.size({one : 1, two : 2, three : 3}), 3, 'can compute the size of an object');
equal(_.size({one: 1, two: 2, three: 3}), 3, 'can compute the size of an object');
equal(_.size([1, 2, 3]), 3, 'can compute the size of an array');
equal(_.size({length: 3, 0: 0, 1: 0, 2: 0}), 3, 'can compute the size of Array-likes');
@@ -834,23 +850,23 @@
if (typeof document != 'undefined') {
test('Can use various collection methods on NodeLists', function() {
var parent = document.createElement('div');
parent.innerHTML = '<span id=id1></span>textnode<span id=id2></span>';
var parent = document.createElement('div');
parent.innerHTML = '<span id=id1></span>textnode<span id=id2></span>';
var elementChildren = _.filter(parent.childNodes, _.isElement);
equal(elementChildren.length, 2);
var elementChildren = _.filter(parent.childNodes, _.isElement);
equal(elementChildren.length, 2);
deepEqual(_.map(elementChildren, 'id'), ['id1', 'id2']);
deepEqual(_.map(parent.childNodes, 'nodeType'), [1, 3, 1]);
deepEqual(_.map(elementChildren, 'id'), ['id1', 'id2']);
deepEqual(_.map(parent.childNodes, 'nodeType'), [1, 3, 1]);
ok(!_.every(parent.childNodes, _.isElement));
ok(_.some(parent.childNodes, _.isElement));
ok(!_.every(parent.childNodes, _.isElement));
ok(_.some(parent.childNodes, _.isElement));
function compareNode(node) {
return _.isElement(node) ? node.id.charAt(2) : void 0;
}
equal(_.max(parent.childNodes, compareNode), _.last(parent.childNodes));
equal(_.min(parent.childNodes, compareNode), _.first(parent.childNodes));
function compareNode(node) {
return _.isElement(node) ? node.id.charAt(2) : void 0;
}
equal(_.max(parent.childNodes, compareNode), _.last(parent.childNodes));
equal(_.min(parent.childNodes, compareNode), _.first(parent.childNodes));
});
}

View File

@@ -138,4 +138,4 @@
});
}
}());
}());

View File

@@ -5,7 +5,7 @@
QUnit.config.asyncRetries = 3;
test('bind', function() {
var context = {name : 'moe'};
var context = {name: 'moe'};
var func = function(arg) { return 'name: ' + (this.name || arg); };
var bound = _.bind(func, context);
equal(bound(), 'name: moe', 'can bind a function to a context');
@@ -29,18 +29,18 @@
func = _.bind(func, this, 'hello', 'moe', 'curly');
equal(func(), 'hello: moe curly', 'the function was partially applied in advance and can accept multiple arguments');
func = function(context, message) { equal(this, context, message); };
func = function(ctx, message) { equal(this, ctx, message); };
_.bind(func, 0, 0, 'can bind a function to `0`')();
_.bind(func, '', '', 'can bind a function to an empty string')();
_.bind(func, false, false, 'can bind a function to `false`')();
// These tests are only meaningful when using a browser without a native bind function
// To test this with a modern browser, set underscore's nativeBind to undefined
var F = function () { return this; };
var F = function() { return this; };
var boundf = _.bind(F, {hello: 'moe curly'});
var Boundf = boundf; // make eslint happy.
var newBoundf = new Boundf();
equal(newBoundf.hello, undefined, 'function should not be bound to the context, to comply with ECMAScript 5');
equal(newBoundf.hello, void 0, 'function should not be bound to the context, to comply with ECMAScript 5');
equal(boundf().hello, 'moe curly', "When called without the new operator, it's OK to be bound to the context");
ok(newBoundf instanceof F, 'a bound instance is an instance of the original function');
@@ -77,13 +77,23 @@
ok(widget instanceof MyWidget, 'Can partially bind a constructor');
equal(widget.get(), 'foo', 'keeps prototype');
deepEqual(widget.options, {a: 1});
_.partial.placeholder = obj;
func = _.partial(function() { return arguments.length; }, obj, 'b', obj, 'd');
equal(func('a'), 4, 'allows the placeholder to be swapped out');
_.partial.placeholder = {};
func = _.partial(function() { return arguments.length; }, obj, 'b', obj, 'd');
equal(func('a'), 5, 'swapping the placeholder preserves previously bound arguments');
_.partial.placeholder = _;
});
test('bindAll', function() {
var curly = {name : 'curly'}, moe = {
name : 'moe',
getName : function() { return 'name: ' + this.name; },
sayHi : function() { return 'hi: ' + this.name; }
var curly = {name: 'curly'}, moe = {
name: 'moe',
getName: function() { return 'name: ' + this.name; },
sayHi: function() { return 'hi: ' + this.name; }
};
curly.getName = moe.getName;
_.bindAll(moe, 'getName', 'sayHi');
@@ -91,12 +101,12 @@
equal(curly.getName(), 'name: curly', 'unbound function is bound to current object');
equal(curly.sayHi(), 'hi: moe', 'bound function is still bound to original object');
curly = {name : 'curly'};
curly = {name: 'curly'};
moe = {
name : 'moe',
getName : function() { return 'name: ' + this.name; },
sayHi : function() { return 'hi: ' + this.name; },
sayLast : function() { return this.sayHi(_.last(arguments)); }
name: 'moe',
getName: function() { return 'name: ' + this.name; },
sayHi: function() { return 'hi: ' + this.name; },
sayLast: function() { return this.sayHi(_.last(arguments)); }
};
throws(function() { _.bindAll(moe); }, Error, 'throws an error for bindAll with no functions named');
@@ -109,6 +119,10 @@
var sayLast = moe.sayLast;
equal(sayLast(1, 2, 3, 4, 5, 6, 7, 'Tom'), 'hi: moe', 'createCallback works with any number of arguments');
_.bindAll(moe, ['getName']);
var getName = moe.getName;
equal(getName(), 'name: moe', 'flattens arguments into a single list');
});
test('memoize', function() {
@@ -145,7 +159,7 @@
return key.toUpperCase();
});
hashed('yep');
deepEqual(hashed.cache, {'YEP': 'yep'}, 'takes a hasher');
deepEqual(hashed.cache, {YEP: 'yep'}, 'takes a hasher');
// Test that the hash function can be used to swizzle the key.
var objCacher = _.memoize(function(value, key) {
@@ -155,7 +169,7 @@
});
var myObj = objCacher('a', 'alpha');
var myObjAlias = objCacher('b', 'alpha');
notStrictEqual(myObj, undefined, 'object is created if second argument used as key');
notStrictEqual(myObj, void 0, 'object is created if second argument used as key');
strictEqual(myObj, myObjAlias, 'object is cached if second argument used as key');
strictEqual(myObj.value, 'a', 'object is not modified if second argument used as key');
});
@@ -346,7 +360,7 @@
throttledIncr();
equal(counter, 1);
_.now = function () {
_.now = function() {
return new Date(2013, 0, 1, 1, 1, 1);
};
@@ -427,7 +441,7 @@
debouncedIncr();
equal(counter, 1, 'incr was called immediately');
_.now = function () {
_.now = function() {
return new Date(2013, 0, 1, 1, 1, 1);
};
@@ -485,13 +499,13 @@
equal(backwards('moe'), 'hi: moe eom', 'wrapped the salutation function');
var inner = function(){ return 'Hello '; };
var obj = {name : 'Moe'};
obj.hi = _.wrap(inner, function(fn){ return fn() + this.name; });
var obj = {name: 'Moe'};
obj.hi = _.wrap(inner, function(fn){ return fn() + this.name; });
equal(obj.hi(), 'Hello Moe');
var noop = function(){};
var noop = function(){};
var wrapped = _.wrap(noop, function(){ return Array.prototype.slice.call(arguments, 0); });
var ret = wrapped(['whats', 'your'], 'vector', 'victor');
var ret = wrapped(['whats', 'your'], 'vector', 'victor');
deepEqual(ret, [noop, ['whats', 'your'], 'vector', 'victor']);
});
@@ -575,7 +589,35 @@
deepEqual(_.toArray(cb(1, 2, 3)), _.range(1, 4));
deepEqual(_.toArray(cb(1, 2, 3, 4, 5, 6, 7, 8, 9, 10)), _.range(1, 11));
});
});
test('restArgs', 10, function() {
_.restArgs(function(a, args) {
strictEqual(a, 1);
deepEqual(args, [2, 3], 'collects rest arguments into an array');
})(1, 2, 3);
_.restArgs(function(a, args) {
strictEqual(a, void 0);
deepEqual(args, [], 'passes empty array if there are not enough arguments');
})();
_.restArgs(function(a, b, c, args) {
strictEqual(arguments.length, 4);
deepEqual(args, [4, 5], 'works on functions with many named parameters');
})(1, 2, 3, 4, 5);
var obj = {};
_.restArgs(function() {
strictEqual(this, obj, 'invokes function with this context');
}).call(obj);
_.restArgs(function(array, iteratee, context) {
deepEqual(array, [1, 2, 3, 4], 'startIndex can be used manually specify index of rest parameter');
strictEqual(iteratee, void 0);
strictEqual(context, void 0);
}, 0)(1, 2, 3, 4);
});
}());

View File

@@ -6,7 +6,7 @@
var testElement = typeof document === 'object' ? document.createElement('div') : void 0;
test('keys', function() {
deepEqual(_.keys({one : 1, two : 2}), ['one', 'two'], 'can extract the keys from an object');
deepEqual(_.keys({one: 1, two: 2}), ['one', 'two'], 'can extract the keys from an object');
// the test above is not safe because it relies on for-in enumeration order
var a = []; a[1] = 0;
deepEqual(_.keys(a), ['1'], 'is not fooled by sparse arrays; see issue #95');
@@ -18,17 +18,17 @@
// keys that may be missed if the implementation isn't careful
var trouble = {
'constructor': Object,
'valueOf': _.noop,
'hasOwnProperty': null,
'toString': 5,
'toLocaleString': undefined,
'propertyIsEnumerable': /a/,
'isPrototypeOf': this,
'__defineGetter__': Boolean,
'__defineSetter__': {},
'__lookupSetter__': false,
'__lookupGetter__': []
constructor: Object,
valueOf: _.noop,
hasOwnProperty: null,
toString: 5,
toLocaleString: void 0,
propertyIsEnumerable: /a/,
isPrototypeOf: this,
__defineGetter__: Boolean,
__defineSetter__: {},
__lookupSetter__: false,
__lookupGetter__: []
};
var troubleKeys = ['constructor', 'valueOf', 'hasOwnProperty', 'toString', 'toLocaleString', 'propertyIsEnumerable',
'isPrototypeOf', '__defineGetter__', '__defineSetter__', '__lookupSetter__', '__lookupGetter__'].sort();
@@ -36,7 +36,7 @@
});
test('allKeys', function() {
deepEqual(_.allKeys({one : 1, two : 2}), ['one', 'two'], 'can extract the allKeys from an object');
deepEqual(_.allKeys({one: 1, two: 2}), ['one', 'two'], 'can extract the allKeys from an object');
// the test above is not safe because it relies on for-in enumeration order
var a = []; a[1] = 0;
deepEqual(_.allKeys(a), ['1'], 'is not fooled by sparse arrays; see issue #95');
@@ -54,7 +54,7 @@
valueOf: _.noop,
hasOwnProperty: null,
toString: 5,
toLocaleString: undefined,
toLocaleString: void 0,
propertyIsEnumerable: /a/,
isPrototypeOf: this
};
@@ -93,7 +93,7 @@
});
test('functions', function() {
var obj = {a : 'dash', b : _.map, c : /yo/, d : _.reduce};
var obj = {a: 'dash', b: _.map, c: /yo/, d: _.reduce};
deepEqual(['b', 'd'], _.functions(obj), 'can grab the function names of any passed-in object');
var Animal = function(){};
@@ -127,13 +127,13 @@
try {
result = {};
_.extend(result, null, undefined, {a: 1});
} catch(ex) {}
_.extend(result, null, void 0, {a: 1});
} catch(e) { /* ignored */ }
equal(result.a, 1, 'should not error on `null` or `undefined` sources');
strictEqual(_.extend(null, {a: 1}), null, 'extending null results in null');
strictEqual(_.extend(undefined, {a: 1}), undefined, 'extending undefined results in undefined');
strictEqual(_.extend(void 0, {a: 1}), void 0, 'extending undefined results in undefined');
});
test('extendOwn', function() {
@@ -154,13 +154,13 @@
deepEqual(_.extendOwn({}, subObj), {c: 'd'}, 'assign copies own properties from source');
result = {};
deepEqual(_.assign(result, null, undefined, {a: 1}), {a: 1}, 'should not error on `null` or `undefined` sources');
deepEqual(_.assign(result, null, void 0, {a: 1}), {a: 1}, 'should not error on `null` or `undefined` sources');
_.each(['a', 5, null, false], function(val) {
strictEqual(_.assign(val, {a: 1}), val, 'assigning non-objects results in returning the non-object value');
});
strictEqual(_.extendOwn(undefined, {a: 1}), undefined, 'assigning undefined results in undefined');
strictEqual(_.extendOwn(void 0, {a: 1}), void 0, 'assigning undefined results in undefined');
result = _.extendOwn({a: 1, 0: 2, 1: '5', length: 6}, {0: 1, 1: 2, length: 2});
deepEqual(result, {a: 1, 0: 1, 1: 2, length: 2}, 'assign should treat array-like objects like normal objects');
@@ -219,7 +219,7 @@
deepEqual(result, {1: 'b'}, 'can omit numeric properties');
deepEqual(_.omit(null, 'a', 'b'), {}, 'non objects return empty object');
deepEqual(_.omit(undefined, 'toString'), {}, 'null/undefined return empty object');
deepEqual(_.omit(void 0, 'toString'), {}, 'null/undefined return empty object');
deepEqual(_.omit(5, 'toString', 'b'), {}, 'returns empty object for primitives');
var data = {a: 1, b: 2, c: 3};
@@ -257,17 +257,17 @@
try {
options = {};
_.defaults(options, null, undefined, {a: 1});
} catch(ex) {}
_.defaults(options, null, void 0, {a: 1});
} catch(e) { /* ignored */ }
equal(options.a, 1, 'should not error on `null` or `undefined` sources');
strictEqual(_.defaults(null, {a: 1}), null, 'result is null if destination is null');
strictEqual(_.defaults(undefined, {a: 1}), undefined, 'result is undefined if destination is undefined');
deepEqual(_.defaults(null, {a: 1}), {a: 1}, 'defaults skips nulls');
deepEqual(_.defaults(void 0, {a: 1}), {a: 1}, 'defaults skips undefined');
});
test('clone', function() {
var moe = {name : 'moe', lucky : [13, 27, 34]};
var moe = {name: 'moe', lucky: [13, 27, 34]};
var clone = _.clone(moe);
equal(clone.name, 'moe', 'the clone as the attributes of the original');
@@ -277,7 +277,7 @@
clone.lucky.push(101);
equal(_.last(moe.lucky), 101, 'changes to deep attributes are shared with the original');
equal(_.clone(undefined), void 0, 'non objects should not be changed by clone');
equal(_.clone(void 0), void 0, 'non objects should not be changed by clone');
equal(_.clone(1), 1, 'non objects should not be changed by clone');
equal(_.clone(null), null, 'non objects should not be changed by clone');
});
@@ -286,7 +286,7 @@
var Parent = function() {};
Parent.prototype = {foo: function() {}, bar: 2};
_.each(['foo', null, undefined, 1], function(val) {
_.each(['foo', null, void 0, 1], function(val) {
deepEqual(_.create(val), {}, 'should return empty object when a non-object is provided');
});
@@ -324,8 +324,8 @@
ok(!_.isEqual(0, -0), '`0` is not equal to `-0`');
ok(!_.isEqual(-0, 0), 'Commutative equality is implemented for `0` and `-0`');
ok(!_.isEqual(null, undefined), '`null` is not equal to `undefined`');
ok(!_.isEqual(undefined, null), 'Commutative equality is implemented for `null` and `undefined`');
ok(!_.isEqual(null, void 0), '`null` is not equal to `undefined`');
ok(!_.isEqual(void 0, null), 'Commutative equality is implemented for `null` and `undefined`');
// String object and primitive comparisons.
ok(_.isEqual('Curly', 'Curly'), 'Identical string primitives are equal');
@@ -350,7 +350,7 @@
// Comparisons involving `NaN`.
ok(_.isEqual(NaN, NaN), '`NaN` is equal to `NaN`');
ok(_.isEqual(new Object(NaN), NaN), 'Object(`NaN`) is equal to `NaN`');
ok(_.isEqual(new Number(NaN), NaN), 'Object(`NaN`) is equal to `NaN`');
ok(!_.isEqual(61, NaN), 'A number primitive is not equal to `NaN`');
ok(!_.isEqual(new Number(79), NaN), 'A number object is not equal to `NaN`');
ok(!_.isEqual(Infinity, NaN), '`Infinity` is not equal to `NaN`');
@@ -431,7 +431,7 @@
var sparse = [];
sparse[1] = 5;
ok(_.isEqual(sparse, [undefined, 5]), 'Handles sparse arrays as dense');
ok(_.isEqual(sparse, [void 0, 5]), 'Handles sparse arrays as dense');
// Simple objects.
ok(_.isEqual({a: 'Curly', b: 1, c: true}, {a: 'Curly', b: 1, c: true}), 'Objects containing identical primitives are equal');
@@ -440,7 +440,7 @@
ok(!_.isEqual({a: 63, b: 75}, {a: 61, c: 55}), 'Objects of identical sizes with different property names are not equal');
ok(!_.isEqual({a: 1, b: 2}, {a: 1}), 'Objects of different sizes are not equal');
ok(!_.isEqual({a: 1}, {a: 1, b: 2}), 'Commutative equality is implemented for objects');
ok(!_.isEqual({x: 1, y: undefined}, {x: 1, z: 2}), 'Objects with identical keys and different values are not equivalent');
ok(!_.isEqual({x: 1, y: void 0}, {x: 1, z: 2}), 'Objects with identical keys and different values are not equivalent');
// `A` contains nested objects and arrays.
a = {
@@ -536,7 +536,7 @@
ok(_.isEqual(a, b), 'Cyclic structures with nested and identically-named properties are equal');
// Chaining.
ok(!_.isEqual(_({x: 1, y: undefined}).chain(), _({x: 1, z: 2}).chain()), 'Chained objects containing different values are not equal');
ok(!_.isEqual(_({x: 1, y: void 0}).chain(), _({x: 1, z: 2}).chain()), 'Chained objects containing different values are not equal');
a = _({x: 1, y: 2}).chain();
b = _({x: 1, y: 2}).chain();
@@ -544,9 +544,9 @@
// Objects without a `constructor` property
if (Object.create) {
a = Object.create(null, {x: {value: 1, enumerable: true}});
b = {x: 1};
ok(_.isEqual(a, b), 'Handles objects without a constructor (e.g. from Object.create');
a = Object.create(null, {x: {value: 1, enumerable: true}});
b = {x: 1};
ok(_.isEqual(a, b), 'Handles objects without a constructor (e.g. from Object.create');
}
function Foo() { this.a = 1; }
@@ -554,12 +554,19 @@
var other = {a: 1};
strictEqual(_.isEqual(new Foo, other), false, 'Objects from different constructors are not equal');
// Tricky object cases val comparisions
equal(_.isEqual([0], [-0]), false);
equal(_.isEqual({a: 0}, {a: -0}), false);
equal(_.isEqual([NaN], [NaN]), true);
equal(_.isEqual({a: NaN}, {a: NaN}), true);
});
test('isEmpty', function() {
ok(!_([1]).isEmpty(), '[1] is not empty');
ok(_.isEmpty([]), '[] is empty');
ok(!_.isEmpty({one : 1}), '{one : 1} is not empty');
ok(!_.isEmpty({one: 1}), '{one: 1} is not empty');
ok(_.isEmpty({}), '{} is empty');
ok(_.isEmpty(new RegExp('')), 'objects with prototype properties are empty');
ok(_.isEmpty(null), 'null is empty');
@@ -567,7 +574,7 @@
ok(_.isEmpty(''), 'the empty string is empty');
ok(!_.isEmpty('moe'), 'but other strings are not');
var obj = {one : 1};
var obj = {one: 1};
delete obj.one;
ok(_.isEmpty(obj), 'deleting all the keys from an object empties it');
@@ -576,7 +583,7 @@
ok(!_.isEmpty(args('')), 'non-empty arguments object is not empty');
// covers collecting non-enumerable properties in IE < 9
var nonEnumProp = {'toString': 5};
var nonEnumProp = {toString: 5};
ok(!_.isEmpty(nonEnumProp), 'non-enumerable property is not empty');
});
@@ -602,9 +609,9 @@
if (testElement) {
ok(_.isObject(testElement), 'and DOM element');
}
ok(_.isObject(function () {}), 'and functions');
ok(_.isObject(function() {}), 'and functions');
ok(!_.isObject(null), 'but not null');
ok(!_.isObject(undefined), 'and not undefined');
ok(!_.isObject(void 0), 'and not undefined');
ok(!_.isObject('string'), 'and not string');
ok(!_.isObject(12), 'and not number');
ok(!_.isObject(true), 'and not boolean');
@@ -612,7 +619,7 @@
});
test('isArray', function() {
ok(!_.isArray(undefined), 'undefined vars are not arrays');
ok(!_.isArray(void 0), 'undefined vars are not arrays');
ok(!_.isArray(arguments), 'the arguments object is not an array');
ok(_.isArray([1, 2, 3]), 'but arrays are');
});
@@ -631,7 +638,7 @@
test('isNumber', function() {
ok(!_.isNumber('string'), 'a string is not a number');
ok(!_.isNumber(arguments), 'the arguments object is not a number');
ok(!_.isNumber(undefined), 'undefined is not a number');
ok(!_.isNumber(void 0), 'undefined is not a number');
ok(_.isNumber(3 * 4 - 7 / 10), 'but numbers are');
ok(_.isNumber(NaN), 'NaN *is* a number');
ok(_.isNumber(Infinity), 'Infinity is a number');
@@ -644,7 +651,7 @@
ok(!_.isBoolean('false'), 'the string "false" is not a boolean');
ok(!_.isBoolean('true'), 'the string "true" is not a boolean');
ok(!_.isBoolean(arguments), 'the arguments object is not a boolean');
ok(!_.isBoolean(undefined), 'undefined is not a boolean');
ok(!_.isBoolean(void 0), 'undefined is not a boolean');
ok(!_.isBoolean(NaN), 'NaN is not a boolean');
ok(!_.isBoolean(null), 'null is not a boolean');
ok(_.isBoolean(true), 'but true is');
@@ -652,7 +659,7 @@
});
test('isFunction', function() {
ok(!_.isFunction(undefined), 'undefined vars are not functions');
ok(!_.isFunction(void 0), 'undefined vars are not functions');
ok(!_.isFunction([1, 2, 3]), 'arrays are not functions');
ok(!_.isFunction('moe'), 'strings are not functions');
ok(_.isFunction(_.isFunction), 'but functions are');
@@ -661,6 +668,11 @@
if (testElement) {
ok(!_.isFunction(testElement), 'elements are not functions');
}
var nodelist = typeof document != 'undefined' && document.childNodes;
if (nodelist) {
ok(!_.isFunction(nodelist));
}
});
if (typeof Int8Array !== 'undefined') {
@@ -669,9 +681,9 @@
.map(_.propertyOf(typeof GLOBAL != 'undefined' ? GLOBAL : window))
.compact()
.each(function(TypedArray) {
// PhantomJS reports `typeof UInt8Array == 'object'` and doesn't report toString TypeArray
// as a function
strictEqual(_.isFunction(TypedArray), Object.prototype.toString.call(TypedArray) === '[object Function]');
// PhantomJS reports `typeof UInt8Array == 'object'` and doesn't report toString TypeArray
// as a function
strictEqual(_.isFunction(TypedArray), Object.prototype.toString.call(TypedArray) === '[object Function]');
});
});
}
@@ -688,7 +700,7 @@
});
test('isFinite', function() {
ok(!_.isFinite(undefined), 'undefined is not finite');
ok(!_.isFinite(void 0), 'undefined is not finite');
ok(!_.isFinite(null), 'null is not finite');
ok(!_.isFinite(NaN), 'NaN is not finite');
ok(!_.isFinite(Infinity), 'Infinity is not finite');
@@ -704,15 +716,16 @@
});
test('isNaN', function() {
ok(!_.isNaN(undefined), 'undefined is not NaN');
ok(!_.isNaN(void 0), 'undefined is not NaN');
ok(!_.isNaN(null), 'null is not NaN');
ok(!_.isNaN(0), '0 is not NaN');
ok(!_.isNaN(new Number(0)), 'wrapped 0 is not NaN');
ok(_.isNaN(NaN), 'but NaN is');
ok(_.isNaN(new Number(NaN)), 'wrapped NaN is still NaN');
});
test('isNull', function() {
ok(!_.isNull(undefined), 'undefined is not null');
ok(!_.isNull(void 0), 'undefined is not null');
ok(!_.isNull(NaN), 'NaN is not null');
ok(_.isNull(null), 'but null is');
});
@@ -723,7 +736,7 @@
ok(!_.isUndefined(false), 'false is defined');
ok(!_.isUndefined(NaN), 'NaN is defined');
ok(_.isUndefined(), 'nothing is undefined');
ok(_.isUndefined(undefined), 'undefined is undefined');
ok(_.isUndefined(void 0), 'undefined is undefined');
});
test('isError', function() {
@@ -755,7 +768,7 @@
equal(intercepted, returned, 'can use tapped objects in a chain');
});
test('has', function () {
test('has', function() {
var obj = {foo: 'bar', func: function(){}};
ok(_.has(obj, 'foo'), 'has() checks that the object has a property.');
ok(!_.has(obj, 'baz'), "has() returns false if the object doesn't have the property.");
@@ -766,7 +779,7 @@
child.prototype = obj;
ok(!_.has(child, 'foo'), 'has() does not check the prototype chain for a property.');
strictEqual(_.has(null, 'foo'), false, 'has() returns false for null');
strictEqual(_.has(undefined, 'foo'), false, 'has() returns false for undefined');
strictEqual(_.has(void 0, 'foo'), false, 'has() returns false for undefined');
});
test('isMatch', function() {
@@ -776,17 +789,17 @@
equal(_.isMatch(moe, {hair: true}), true, 'Returns a boolean');
equal(_.isMatch(curly, {hair: true}), false, 'Returns a boolean');
equal(_.isMatch(5, {__x__: undefined}), false, 'can match undefined props on primitives');
equal(_.isMatch({__x__: undefined}, {__x__: undefined}), true, 'can match undefined props');
equal(_.isMatch(5, {__x__: void 0}), false, 'can match undefined props on primitives');
equal(_.isMatch({__x__: void 0}, {__x__: void 0}), true, 'can match undefined props');
equal(_.isMatch(null, {}), true, 'Empty spec called with null object returns true');
equal(_.isMatch(null, {a: 1}), false, 'Non-empty spec called with null object returns false');
_.each([null, undefined], function(item) { strictEqual(_.isMatch(item, null), true, 'null matches null'); });
_.each([null, undefined], function(item) { strictEqual(_.isMatch(item, null), true, 'null matches {}'); });
strictEqual(_.isMatch({b: 1}, {a: undefined}), false, 'handles undefined values (1683)');
_.each([null, void 0], function(item) { strictEqual(_.isMatch(item, null), true, 'null matches null'); });
_.each([null, void 0], function(item) { strictEqual(_.isMatch(item, null), true, 'null matches {}'); });
strictEqual(_.isMatch({b: 1}, {a: void 0}), false, 'handles undefined values (1683)');
_.each([true, 5, NaN, null, undefined], function(item) {
_.each([true, 5, NaN, null, void 0], function(item) {
strictEqual(_.isMatch({a: 1}, item), true, 'treats primitives as empty');
});
@@ -805,8 +818,8 @@
ok(_.isMatch({x: 5, y: 1}, Prototest), 'spec can be a function');
//null edge cases
var oCon = {'constructor': Object};
deepEqual(_.map([null, undefined, 5, {}], _.partial(_.isMatch, _, oCon)), [false, false, false, true], 'doesnt falsey match constructor on undefined/null');
var oCon = {constructor: Object};
deepEqual(_.map([null, void 0, 5, {}], _.partial(_.isMatch, _, oCon)), [false, false, false, true], 'doesnt falsey match constructor on undefined/null');
});
test('matcher', function() {
@@ -817,21 +830,21 @@
equal(_.matcher({hair: true})(moe), true, 'Returns a boolean');
equal(_.matcher({hair: true})(curly), false, 'Returns a boolean');
equal(_.matcher({__x__: undefined})(5), false, 'can match undefined props on primitives');
equal(_.matcher({__x__: undefined})({__x__: undefined}), true, 'can match undefined props');
equal(_.matcher({__x__: void 0})(5), false, 'can match undefined props on primitives');
equal(_.matcher({__x__: void 0})({__x__: void 0}), true, 'can match undefined props');
equal(_.matcher({})(null), true, 'Empty spec called with null object returns true');
equal(_.matcher({a: 1})(null), false, 'Non-empty spec called with null object returns false');
ok(_.find(stooges, _.matcher({hair: false})) === curly, 'returns a predicate that can be used by finding functions.');
ok(_.find(stooges, _.matcher(moe)) === moe, 'can be used to locate an object exists in a collection.');
deepEqual(_.where([null, undefined], {a: 1}), [], 'Do not throw on null values.');
deepEqual(_.where([null, void 0], {a: 1}), [], 'Do not throw on null values.');
deepEqual(_.where([null, undefined], null), [null, undefined], 'null matches null');
deepEqual(_.where([null, undefined], {}), [null, undefined], 'null matches {}');
deepEqual(_.where([{b: 1}], {a: undefined}), [], 'handles undefined values (1683)');
deepEqual(_.where([null, void 0], null), [null, void 0], 'null matches null');
deepEqual(_.where([null, void 0], {}), [null, void 0], 'null matches {}');
deepEqual(_.where([{b: 1}], {a: void 0}), [], 'handles undefined values (1683)');
_.each([true, 5, NaN, null, undefined], function(item) {
_.each([true, 5, NaN, null, void 0], function(item) {
deepEqual(_.where([{a: 1}], item), [{a: 1}], 'treats primitives as empty');
});
@@ -852,82 +865,25 @@
ok(_.matcher(Prototest)({x: 5, y: 1}), 'spec can be a function');
// #1729
var o = {'b': 1};
var o = {b: 1};
var m = _.matcher(o);
equal(m({'b': 1}), true);
equal(m({b: 1}), true);
o.b = 2;
o.a = 1;
equal(m({'b': 1}), true, 'changing spec object doesnt change matches result');
equal(m({b: 1}), true, 'changing spec object doesnt change matches result');
//null edge cases
var oCon = _.matcher({'constructor': Object});
deepEqual(_.map([null, undefined, 5, {}], oCon), [false, false, false, true], 'doesnt falsey match constructor on undefined/null');
});
test('matcher', function() {
var moe = {name: 'Moe Howard', hair: true};
var curly = {name: 'Curly Howard', hair: false};
var stooges = [moe, curly];
equal(_.matcher({hair: true})(moe), true, 'Returns a boolean');
equal(_.matcher({hair: true})(curly), false, 'Returns a boolean');
equal(_.matcher({__x__: undefined})(5), false, 'can match undefined props on primitives');
equal(_.matcher({__x__: undefined})({__x__: undefined}), true, 'can match undefined props');
equal(_.matcher({})(null), true, 'Empty spec called with null object returns true');
equal(_.matcher({a: 1})(null), false, 'Non-empty spec called with null object returns false');
ok(_.find(stooges, _.matcher({hair: false})) === curly, 'returns a predicate that can be used by finding functions.');
ok(_.find(stooges, _.matcher(moe)) === moe, 'can be used to locate an object exists in a collection.');
deepEqual(_.where([null, undefined], {a: 1}), [], 'Do not throw on null values.');
deepEqual(_.where([null, undefined], null), [null, undefined], 'null matches null');
deepEqual(_.where([null, undefined], {}), [null, undefined], 'null matches {}');
deepEqual(_.where([{b: 1}], {a: undefined}), [], 'handles undefined values (1683)');
_.each([true, 5, NaN, null, undefined], function(item) {
deepEqual(_.where([{a: 1}], item), [{a: 1}], 'treats primitives as empty');
});
function Prototest() {}
Prototest.prototype.x = 1;
var specObj = new Prototest;
var protospec = _.matcher(specObj);
equal(protospec({x: 2}), true, 'spec is restricted to own properties');
specObj.y = 5;
protospec = _.matcher(specObj);
equal(protospec({x: 1, y: 5}), true);
equal(protospec({x: 1, y: 4}), false);
ok(_.matcher({x: 1, y: 5})(specObj), 'inherited and own properties are checked on the test object');
Prototest.x = 5;
ok(_.matcher(Prototest)({x: 5, y: 1}), 'spec can be a function');
// #1729
var o = {'b': 1};
var m = _.matcher(o);
equal(m({'b': 1}), true);
o.b = 2;
o.a = 1;
equal(m({'b': 1}), true, 'changing spec object doesnt change matches result');
//null edge cases
var oCon = _.matcher({'constructor': Object});
deepEqual(_.map([null, undefined, 5, {}], oCon), [false, false, false, true], 'doesnt falsey match constructor on undefined/null');
var oCon = _.matcher({constructor: Object});
deepEqual(_.map([null, void 0, 5, {}], oCon), [false, false, false, true], 'doesnt falsey match constructor on undefined/null');
});
test('findKey', function() {
var objects = {
a: {'a': 0, 'b': 0},
b: {'a': 1, 'b': 1},
c: {'a': 2, 'b': 2}
a: {a: 0, b: 0},
b: {a: 1, b: 1},
c: {a: 2, b: 2}
};
equal(_.findKey(objects, function(obj) {
@@ -942,7 +898,7 @@
equal(_.findKey(objects, function(obj) {
return obj.b * obj.a === 5;
}), undefined);
}), void 0);
strictEqual(_.findKey([1, 2, 3, 4, 5, 6], function(obj) {
return obj === 3;
@@ -950,7 +906,7 @@
strictEqual(_.findKey(objects, function(a) {
return a.foo === null;
}), undefined);
}), void 0);
_.findKey({a: {a: 1}}, function(a, key, obj) {
equal(key, 'a');
@@ -965,53 +921,53 @@
test('mapObject', function() {
var obj = {'a': 1, 'b': 2};
var objects = {
a: {'a': 0, 'b': 0},
b: {'a': 1, 'b': 1},
c: {'a': 2, 'b': 2}
var obj = {a: 1, b: 2};
var objects = {
a: {a: 0, b: 0},
b: {a: 1, b: 1},
c: {a: 2, b: 2}
};
deepEqual(_.mapObject(obj, function(val) {
return val * 2;
}), {'a': 2, 'b': 4}, 'simple objects');
}), {a: 2, b: 4}, 'simple objects');
deepEqual(_.mapObject(objects, function(val) {
return _.reduce(val, function(memo,v){
return memo + v;
},0);
}), {'a': 0, 'b': 2, 'c': 4}, 'nested objects');
return _.reduce(val, function(memo, v){
return memo + v;
}, 0);
}), {a: 0, b: 2, c: 4}, 'nested objects');
deepEqual(_.mapObject(obj, function(val,key,obj) {
return obj[key] * 2;
}), {'a': 2, 'b': 4}, 'correct keys');
deepEqual(_.mapObject(obj, function(val, key, o) {
return o[key] * 2;
}), {a: 2, b: 4}, 'correct keys');
deepEqual(_.mapObject([1,2], function(val) {
deepEqual(_.mapObject([1, 2], function(val) {
return val * 2;
}), {'0': 2, '1': 4}, 'check behavior for arrays');
}), {0: 2, 1: 4}, 'check behavior for arrays');
deepEqual(_.mapObject(obj, function(val) {
return val * this.multiplier;
}, {multiplier : 3}), {'a': 3, 'b': 6}, 'keep context');
}, {multiplier: 3}), {a: 3, b: 6}, 'keep context');
deepEqual(_.mapObject({a: 1}, function() {
return this.length;
}, [1,2]), {'a': 2}, 'called with context');
}, [1, 2]), {a: 2}, 'called with context');
var ids = _.mapObject({length: 2, 0: {id: '1'}, 1: {id: '2'}}, function(n){
return n.id;
});
deepEqual(ids, {'length': undefined, '0': '1', '1': '2'}, 'Check with array-like objects');
deepEqual(ids, {length: void 0, 0: '1', 1: '2'}, 'Check with array-like objects');
// Passing a property name like _.pluck.
var people = {'a': {name : 'moe', age : 30}, 'b': {name : 'curly', age : 50}};
deepEqual(_.mapObject(people, 'name'), {'a': 'moe', 'b': 'curly'}, 'predicate string map to object properties');
var people = {a: {name: 'moe', age: 30}, b: {name: 'curly', age: 50}};
deepEqual(_.mapObject(people, 'name'), {a: 'moe', b: 'curly'}, 'predicate string map to object properties');
_.each([null, void 0, 1, 'abc', [], {}, undefined], function(val){
_.each([null, void 0, 1, 'abc', [], {}, void 0], function(val){
deepEqual(_.mapObject(val, _.identity), {}, 'mapValue identity');
});
var Proto = function(){this.a = 1;};
var Proto = function(){ this.a = 1; };
Proto.prototype.b = 1;
var protoObj = new Proto();
deepEqual(_.mapObject(protoObj, _.identity), {a: 1}, 'ignore inherited values from prototypes');

View File

@@ -14,6 +14,37 @@
});
if (typeof this == 'object') {
test('noConflict', function() {
var underscore = _.noConflict();
equal(underscore.identity(1), 1);
if (typeof require != 'function') {
equal(this._, void 0, 'global underscore is removed');
this._ = underscore;
}
});
}
if (typeof require == 'function') {
asyncTest('noConflict (node vm)', 2, function() {
var fs = require('fs');
var vm = require('vm');
var filename = __dirname + '/../underscore.js';
fs.readFile(filename, function(err, content){
var sandbox = vm.createScript(
content + 'this.underscore = this._.noConflict();',
filename
);
var context = {_: 'oldvalue'};
sandbox.runInNewContext(context);
equal(context._, 'oldvalue');
equal(context.underscore.VERSION, _.VERSION);
start();
});
});
}
test('#750 - Return _ instance.', 2, function() {
var instance = _([]);
ok(_(instance) === instance);
@@ -21,42 +52,42 @@
});
test('identity', function() {
var stooge = {name : 'moe'};
var stooge = {name: 'moe'};
equal(_.identity(stooge), stooge, 'stooge is the same as his identity');
});
test('constant', function() {
var stooge = {name : 'moe'};
var stooge = {name: 'moe'};
equal(_.constant(stooge)(), stooge, 'should create a function that returns stooge');
});
test('noop', function() {
strictEqual(_.noop('curly', 'larry', 'moe'), undefined, 'should always return undefined');
strictEqual(_.noop('curly', 'larry', 'moe'), void 0, 'should always return undefined');
});
test('property', function() {
var stooge = {name : 'moe'};
var stooge = {name: 'moe'};
equal(_.property('name')(stooge), 'moe', 'should return the property with the given name');
equal(_.property('name')(null), undefined, 'should return undefined for null values');
equal(_.property('name')(undefined), undefined, 'should return undefined for undefined values');
equal(_.property('name')(null), void 0, 'should return undefined for null values');
equal(_.property('name')(void 0), void 0, 'should return undefined for undefined values');
});
test('propertyOf', function() {
var stoogeRanks = _.propertyOf({curly: 2, moe: 1, larry: 3});
equal(stoogeRanks('curly'), 2, 'should return the property with the given name');
equal(stoogeRanks(null), undefined, 'should return undefined for null values');
equal(stoogeRanks(undefined), undefined, 'should return undefined for undefined values');
equal(stoogeRanks(null), void 0, 'should return undefined for null values');
equal(stoogeRanks(void 0), void 0, 'should return undefined for undefined values');
function MoreStooges() { this.shemp = 87; }
MoreStooges.prototype = {curly: 2, moe: 1, larry: 3};
var moreStoogeRanks = _.propertyOf(new MoreStooges());
equal(moreStoogeRanks('curly'), 2, 'should return properties from further up the prototype chain');
var nullPropertyOf = _.propertyOf(null);
equal(nullPropertyOf('curly'), undefined, 'should return undefined when obj is null');
var undefPropertyOf = _.propertyOf(undefined);
equal(undefPropertyOf('curly'), undefined, 'should return undefined when obj is undefined');
equal(nullPropertyOf('curly'), void 0, 'should return undefined when obj is null');
var undefPropertyOf = _.propertyOf(void 0);
equal(undefPropertyOf('curly'), void 0, 'should return undefined when obj is undefined');
});
test('random', function() {
@@ -86,7 +117,7 @@
test('times', function() {
var vals = [];
_.times(3, function (i) { vals.push(i); });
_.times(3, function(i) { vals.push(i); });
deepEqual(vals, [0, 1, 2], 'is 0 indexed');
//
vals = [];
@@ -127,16 +158,16 @@
var escapeCharacters = ['<', '>', '"', '\'', '`'];
_.each(escapeCharacters, function(escapeChar) {
var str = 'a ' + escapeChar + ' string escaped';
var escaped = _.escape(str);
notEqual(str, escaped, escapeChar + ' is escaped');
equal(str, _.unescape(escaped), escapeChar + ' can be unescaped');
var s = 'a ' + escapeChar + ' string escaped';
var e = _.escape(s);
notEqual(s, e, escapeChar + ' is escaped');
equal(s, _.unescape(e), escapeChar + ' can be unescaped');
str = 'a ' + escapeChar + escapeChar + escapeChar + 'some more string' + escapeChar;
escaped = _.escape(str);
s = 'a ' + escapeChar + escapeChar + escapeChar + 'some more string' + escapeChar;
e = _.escape(s);
equal(escaped.indexOf(escapeChar), -1, 'can escape multiple occurances of ' + escapeChar);
equal(_.unescape(escaped), str, 'multiple occurrences of ' + escapeChar + ' can be unescaped');
equal(e.indexOf(escapeChar), -1, 'can escape multiple occurances of ' + escapeChar);
equal(_.unescape(e), s, 'multiple occurrences of ' + escapeChar + ' can be unescaped');
});
// handles multiple escape characters at once
@@ -158,7 +189,7 @@
test('template', function() {
var basicTemplate = _.template("<%= thing %> is gettin' on my noives!");
var result = basicTemplate({thing : 'This'});
var result = basicTemplate({thing: 'This'});
equal(result, "This is gettin' on my noives!", 'can do basic attribute interpolation');
var sansSemicolonTemplate = _.template('A <% this %> B');
@@ -173,7 +204,7 @@
var fancyTemplate = _.template('<ul><% ' +
' for (var key in people) { ' +
'%><li><%= people[key] %></li><% } %></ul>');
result = fancyTemplate({people : {moe : 'Moe', larry : 'Larry', curly : 'Curly'}});
result = fancyTemplate({people: {moe: 'Moe', larry: 'Larry', curly: 'Curly'}});
equal(result, '<ul><li>Moe</li><li>Larry</li><li>Curly</li></ul>', 'can run arbitrary javascript in templates');
var escapedCharsInJavascriptTemplate = _.template('<ul><% _.each(numbers.split("\\n"), function(item) { %><li><%= item %></li><% }) %></ul>');
@@ -222,15 +253,15 @@
' if (data) { data += 12345; }; %>\n ' +
' <li><%= data %></li>\n '
);
equal(template({data : 12345}).replace(/\s/g, ''), '<li>24690</li>');
equal(template({data: 12345}).replace(/\s/g, ''), '<li>24690</li>');
_.templateSettings = {
evaluate : /\{\{([\s\S]+?)\}\}/g,
interpolate : /\{\{=([\s\S]+?)\}\}/g
evaluate: /\{\{([\s\S]+?)\}\}/g,
interpolate: /\{\{=([\s\S]+?)\}\}/g
};
var custom = _.template('<ul>{{ for (var key in people) { }}<li>{{= people[key] }}</li>{{ } }}</ul>');
result = custom({people : {moe : 'Moe', larry : 'Larry', curly : 'Curly'}});
result = custom({people: {moe: 'Moe', larry: 'Larry', curly: 'Curly'}});
equal(result, '<ul><li>Moe</li><li>Larry</li><li>Curly</li></ul>', 'can run arbitrary javascript in templates');
var customQuote = _.template("It's its, not it's");
@@ -240,12 +271,12 @@
equal(quoteInStatementAndBody({foo: 'bar'}), "Statement quotes and 'quotes'.");
_.templateSettings = {
evaluate : /<\?([\s\S]+?)\?>/g,
interpolate : /<\?=([\s\S]+?)\?>/g
evaluate: /<\?([\s\S]+?)\?>/g,
interpolate: /<\?=([\s\S]+?)\?>/g
};
var customWithSpecialChars = _.template('<ul><? for (var key in people) { ?><li><?= people[key] ?></li><? } ?></ul>');
result = customWithSpecialChars({people : {moe : 'Moe', larry : 'Larry', curly : 'Curly'}});
result = customWithSpecialChars({people: {moe: 'Moe', larry: 'Larry', curly: 'Curly'}});
equal(result, '<ul><li>Moe</li><li>Larry</li><li>Curly</li></ul>', 'can run arbitrary javascript in templates');
var customWithSpecialCharsQuote = _.template("It's its, not it's");
@@ -255,21 +286,22 @@
equal(quoteInStatementAndBody({foo: 'bar'}), "Statement quotes and 'quotes'.");
_.templateSettings = {
interpolate : /\{\{(.+?)\}\}/g
interpolate: /\{\{(.+?)\}\}/g
};
var mustache = _.template('Hello {{planet}}!');
equal(mustache({planet : 'World'}), 'Hello World!', 'can mimic mustache.js');
equal(mustache({planet: 'World'}), 'Hello World!', 'can mimic mustache.js');
var templateWithNull = _.template('a null undefined {{planet}}');
equal(templateWithNull({planet : 'world'}), 'a null undefined world', 'can handle missing escape and evaluate settings');
equal(templateWithNull({planet: 'world'}), 'a null undefined world', 'can handle missing escape and evaluate settings');
});
test('_.template provides the generated function source, when a SyntaxError occurs', function() {
var source;
try {
_.template('<b><%= if x %></b>');
} catch (ex) {
var source = ex.source;
source = ex.source;
}
ok(/__p/.test(source));
});
@@ -284,13 +316,13 @@
strictEqual(_.result(obj, 'w'), '');
strictEqual(_.result(obj, 'x'), 'x');
strictEqual(_.result(obj, 'y'), 'x');
strictEqual(_.result(obj, 'z'), undefined);
strictEqual(_.result(null, 'x'), undefined);
strictEqual(_.result(obj, 'z'), void 0);
strictEqual(_.result(null, 'x'), void 0);
});
test('result returns a default value if object is null or undefined', function() {
strictEqual(_.result(null, 'b', 'default'), 'default');
strictEqual(_.result(undefined, 'c', 'default'), 'default');
strictEqual(_.result(void 0, 'c', 'default'), 'default');
strictEqual(_.result(''.match('missing'), 1, 'default'), 'default');
});
@@ -301,7 +333,7 @@
test('result only returns the default value if the object does not have the property or is undefined', function() {
strictEqual(_.result({}, 'b', 'default'), 'default');
strictEqual(_.result({d: undefined}, 'd', 'default'), 'default');
strictEqual(_.result({d: void 0}, 'd', 'default'), 'default');
});
test('result does not return the default if the property of an object is found in the prototype', function() {
@@ -312,7 +344,7 @@
test('result does use the fallback when the result of invoking the property is undefined', function() {
var obj = {a: function() {}};
strictEqual(_.result(obj, 'a', 'failed'), undefined);
strictEqual(_.result(obj, 'a', 'failed'), void 0);
});
test('result fallback can use a function', function() {
@@ -341,11 +373,11 @@
test('#556 - undefined template variables.', function() {
var template = _.template('<%=x%>');
strictEqual(template({x: null}), '');
strictEqual(template({x: undefined}), '');
strictEqual(template({x: void 0}), '');
var templateEscaped = _.template('<%-x%>');
strictEqual(templateEscaped({x: null}), '');
strictEqual(templateEscaped({x: undefined}), '');
strictEqual(templateEscaped({x: void 0}), '');
var templateWithProperty = _.template('<%=x.foo%>');
strictEqual(templateWithProperty({x: {}}), '');

View File

@@ -8,29 +8,32 @@
// Baseline setup
// --------------
// Establish the root object, `window` in the browser, or `exports` on the server.
var root = this;
// Establish the root object, `window` (`self`) in the browser, `global`
// on the server, or `this` in some virtual machines. We use `self`
// instead of `window` for `WebWorker` support.
var root = typeof self === 'object' && self.self === self && self ||
typeof global === 'object' && global.global === global && global ||
this;
// Save the previous value of the `_` variable.
var previousUnderscore = root._;
// Save bytes in the minified (but not gzipped) version:
var ArrayProto = Array.prototype, ObjProto = Object.prototype, FuncProto = Function.prototype;
var ArrayProto = Array.prototype, ObjProto = Object.prototype;
// Create quick reference variables for speed access to core prototypes.
var
push = ArrayProto.push,
slice = ArrayProto.slice,
toString = ObjProto.toString,
hasOwnProperty = ObjProto.hasOwnProperty;
push = ArrayProto.push,
slice = ArrayProto.slice,
toString = ObjProto.toString,
hasOwnProperty = ObjProto.hasOwnProperty;
// All **ECMAScript 5** native function implementations that we hope to use
// are declared here.
var
nativeIsArray = Array.isArray,
nativeKeys = Object.keys,
nativeBind = FuncProto.bind,
nativeCreate = Object.create;
nativeIsArray = Array.isArray,
nativeKeys = Object.keys,
nativeCreate = Object.create;
// Naked function reference for surrogate-prototype-swapping.
var Ctor = function(){};
@@ -43,7 +46,7 @@
};
// Export the Underscore object for **Node.js**, with
// backwards-compatibility for the old `require()` API. If we're in
// backwards-compatibility for their old module API. If we're in
// the browser, add `_` as a global object.
if (typeof exports !== 'undefined') {
if (typeof module !== 'undefined' && module.exports) {
@@ -66,9 +69,8 @@
case 1: return function(value) {
return func.call(context, value);
};
case 2: return function(value, other) {
return func.call(context, value, other);
};
// The 2-parameter case has been omitted only because no current consumers
// made use of it.
case 3: return function(value, index, collection) {
return func.call(context, value, index, collection);
};
@@ -94,21 +96,27 @@
return cb(value, context, Infinity);
};
// An internal function for creating assigner functions.
var createAssigner = function(keysFunc, undefinedOnly) {
return function(obj) {
var length = arguments.length;
if (length < 2 || obj == null) return obj;
for (var index = 1; index < length; index++) {
var source = arguments[index],
keys = keysFunc(source),
l = keys.length;
for (var i = 0; i < l; i++) {
var key = keys[i];
if (!undefinedOnly || obj[key] === void 0) obj[key] = source[key];
}
// Similar to ES6's rest param (http://ariya.ofilabs.com/2013/03/es6-and-rest-parameter.html)
// This accumulates the arguments passed into an array, after a given index.
var restArgs = function(func, startIndex) {
startIndex = startIndex == null ? func.length - 1 : +startIndex;
return function() {
var length = Math.max(arguments.length - startIndex, 0);
var rest = Array(length);
for (var index = 0; index < length; index++) {
rest[index] = arguments[index + startIndex];
}
return obj;
switch (startIndex) {
case 0: return func.call(this, rest);
case 1: return func.call(this, arguments[0], rest);
case 2: return func.call(this, arguments[0], arguments[1], rest);
}
var args = Array(startIndex + 1);
for (index = 0; index < startIndex; index++) {
args[index] = arguments[index];
}
args[startIndex] = rest;
return func.apply(this, args);
};
};
@@ -175,30 +183,29 @@
};
// Create a reducing function iterating left or right.
function createReduce(dir) {
var createReduce = function(dir) {
// Optimized iterator function as using arguments.length
// in the main function will deoptimize the, see #1991.
function iterator(obj, iteratee, memo, keys, index, length) {
var reducer = function(obj, iteratee, memo, initial) {
var keys = !isArrayLike(obj) && _.keys(obj),
length = (keys || obj).length,
index = dir > 0 ? 0 : length - 1;
if (!initial) {
memo = obj[keys ? keys[index] : index];
index += dir;
}
for (; index >= 0 && index < length; index += dir) {
var currentKey = keys ? keys[index] : index;
memo = iteratee(memo, obj[currentKey], currentKey, obj);
}
return memo;
}
};
return function(obj, iteratee, memo, context) {
iteratee = optimizeCb(iteratee, context, 4);
var keys = !isArrayLike(obj) && _.keys(obj),
length = (keys || obj).length,
index = dir > 0 ? 0 : length - 1;
// Determine the initial value if none is provided.
if (arguments.length < 3) {
memo = obj[keys ? keys[index] : index];
index += dir;
}
return iterator(obj, iteratee, memo, keys, index, length);
var initial = arguments.length >= 3;
return reducer(obj, optimizeCb(iteratee, context, 4), memo, initial);
};
}
};
// **Reduce** builds up a single result from a list of values, aka `inject`,
// or `foldl`.
@@ -269,14 +276,13 @@
};
// Invoke a method (with arguments) on every item in a collection.
_.invoke = function(obj, method) {
var args = slice.call(arguments, 2);
_.invoke = restArgs(function(obj, method, args) {
var isFunc = _.isFunction(method);
return _.map(obj, function(value) {
var func = isFunc ? method : value[method];
return func == null ? func : func.apply(value, args);
});
};
});
// Convenience version of a common use case of `map`: fetching a property.
_.pluck = function(obj, key) {
@@ -299,7 +305,7 @@
_.max = function(obj, iteratee, context) {
var result = -Infinity, lastComputed = -Infinity,
value, computed;
if (iteratee == null && obj != null) {
if (iteratee == null || (typeof iteratee == 'number' && typeof obj[0] != 'object') && obj != null) {
obj = isArrayLike(obj) ? obj : _.values(obj);
for (var i = 0, length = obj.length; i < length; i++) {
value = obj[i];
@@ -309,10 +315,10 @@
}
} else {
iteratee = cb(iteratee, context);
_.each(obj, function(value, index, list) {
computed = iteratee(value, index, list);
_.each(obj, function(v, index, list) {
computed = iteratee(v, index, list);
if (computed > lastComputed || computed === -Infinity && result === -Infinity) {
result = value;
result = v;
lastComputed = computed;
}
});
@@ -324,7 +330,7 @@
_.min = function(obj, iteratee, context) {
var result = Infinity, lastComputed = Infinity,
value, computed;
if (iteratee == null && obj != null) {
if (iteratee == null || (typeof iteratee == 'number' && typeof obj[0] != 'object') && obj != null) {
obj = isArrayLike(obj) ? obj : _.values(obj);
for (var i = 0, length = obj.length; i < length; i++) {
value = obj[i];
@@ -334,10 +340,10 @@
}
} else {
iteratee = cb(iteratee, context);
_.each(obj, function(value, index, list) {
computed = iteratee(value, index, list);
_.each(obj, function(v, index, list) {
computed = iteratee(v, index, list);
if (computed < lastComputed || computed === Infinity && result === Infinity) {
result = value;
result = v;
lastComputed = computed;
}
});
@@ -345,21 +351,13 @@
return result;
};
// Shuffle a collection, using the modern version of the
// [Fisher-Yates shuffle](http://en.wikipedia.org/wiki/FisherYates_shuffle).
// Shuffle a collection.
_.shuffle = function(obj) {
var set = isArrayLike(obj) ? obj : _.values(obj);
var length = set.length;
var shuffled = Array(length);
for (var index = 0, rand; index < length; index++) {
rand = _.random(0, index);
if (rand !== index) shuffled[index] = shuffled[rand];
shuffled[rand] = set[index];
}
return shuffled;
return _.sample(obj, Infinity);
};
// Sample **n** random values from a collection.
// Sample **n** random values from a collection using the modern version of the
// [Fisher-Yates shuffle](http://en.wikipedia.org/wiki/FisherYates_shuffle).
// If **n** is not specified, returns a single random element.
// The internal `guard` argument allows it to work with `map`.
_.sample = function(obj, n, guard) {
@@ -367,17 +365,28 @@
if (!isArrayLike(obj)) obj = _.values(obj);
return obj[_.random(obj.length - 1)];
}
return _.shuffle(obj).slice(0, Math.max(0, n));
var sample = isArrayLike(obj) ? _.clone(obj) : _.values(obj);
var length = getLength(sample);
n = Math.max(Math.min(n, length), 0);
var last = length - 1;
for (var index = 0; index < n; index++) {
var rand = _.random(index, last);
var temp = sample[index];
sample[index] = sample[rand];
sample[rand] = temp;
}
return sample.slice(0, n);
};
// Sort the object's values by a criterion produced by an iteratee.
_.sortBy = function(obj, iteratee, context) {
var index = 0;
iteratee = cb(iteratee, context);
return _.pluck(_.map(obj, function(value, index, list) {
return _.pluck(_.map(obj, function(value, key, list) {
return {
value: value,
index: index,
criteria: iteratee(value, index, list)
index: index++,
criteria: iteratee(value, key, list)
};
}).sort(function(left, right) {
var a = left.criteria;
@@ -391,9 +400,9 @@
};
// An internal function used for aggregate "group by" operations.
var group = function(behavior) {
var group = function(behavior, partition) {
return function(obj, iteratee, context) {
var result = {};
var result = partition ? [[], []] : {};
iteratee = cb(iteratee, context);
_.each(obj, function(value, index) {
var key = iteratee(value, index, obj);
@@ -438,14 +447,9 @@
// Split a collection into two arrays: one whose elements all satisfy the given
// predicate, and one whose elements all do not satisfy the predicate.
_.partition = function(obj, predicate, context) {
predicate = cb(predicate, context);
var pass = [], fail = [];
_.each(obj, function(value, key, obj) {
(predicate(value, key, obj) ? pass : fail).push(value);
});
return [pass, fail];
};
_.partition = group(function(result, value, pass) {
result[pass ? 0 : 1].push(value);
}, true);
// Array Functions
// ---------------
@@ -487,17 +491,19 @@
};
// Internal implementation of a recursive `flatten` function.
var flatten = function(input, shallow, strict, startIndex) {
var output = [], idx = 0;
for (var i = startIndex || 0, length = getLength(input); i < length; i++) {
var flatten = function(input, shallow, strict, output) {
output = output || [];
var idx = output.length;
for (var i = 0, length = getLength(input); i < length; i++) {
var value = input[i];
if (isArrayLike(value) && (_.isArray(value) || _.isArguments(value))) {
//flatten current level of array or arguments object
if (!shallow) value = flatten(value, shallow, strict);
var j = 0, len = value.length;
output.length += len;
while (j < len) {
output[idx++] = value[j++];
if (shallow) {
var j = 0, len = value.length;
while (j < len) output[idx++] = value[j++];
} else {
flatten(value, shallow, strict, output);
idx = output.length;
}
} else if (!strict) {
output[idx++] = value;
@@ -512,9 +518,9 @@
};
// Return a version of the array that does not contain the specified value(s).
_.without = function(array) {
return _.difference(array, slice.call(arguments, 1));
};
_.without = restArgs(function(array, otherArrays) {
return _.difference(array, otherArrays);
});
// Produce a duplicate-free version of the array. If the array has already
// been sorted, you have the option of using a faster algorithm.
@@ -548,9 +554,9 @@
// Produce an array that contains the union: each distinct element from all of
// the passed-in arrays.
_.union = function() {
return _.uniq(flatten(arguments, true, true));
};
_.union = restArgs(function(arrays) {
return _.uniq(flatten(arrays, true, true));
});
// Produce an array that contains every item shared between all the
// passed-in arrays.
@@ -560,7 +566,8 @@
for (var i = 0, length = getLength(array); i < length; i++) {
var item = array[i];
if (_.contains(result, item)) continue;
for (var j = 1; j < argsLength; j++) {
var j;
for (j = 1; j < argsLength; j++) {
if (!_.contains(arguments[j], item)) break;
}
if (j === argsLength) result.push(item);
@@ -570,18 +577,12 @@
// 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 = flatten(arguments, true, true, 1);
_.difference = restArgs(function(array, rest) {
rest = flatten(rest, true, true);
return _.filter(array, function(value){
return !_.contains(rest, value);
});
};
// Zip together multiple lists into a single array -- elements that share
// an index go together.
_.zip = function() {
return _.unzip(arguments);
};
});
// Complement of _.zip. Unzip accepts an array of arrays and groups
// each array's elements on shared indices
@@ -595,6 +596,10 @@
return result;
};
// Zip together multiple lists into a single array -- elements that share
// an index go together.
_.zip = restArgs(_.unzip);
// Converts lists into objects. Pass either a single array of `[key, value]`
// pairs, or two parallel arrays of the same length -- one of keys, and one of
// the corresponding values.
@@ -611,7 +616,7 @@
};
// Generator function to create the findIndex and findLastIndex functions
function createPredicateIndexFinder(dir) {
var createPredicateIndexFinder = function(dir) {
return function(array, predicate, context) {
predicate = cb(predicate, context);
var length = getLength(array);
@@ -621,7 +626,7 @@
}
return -1;
};
}
};
// Returns the first index on an array-like that passes a predicate test
_.findIndex = createPredicateIndexFinder(1);
@@ -641,14 +646,14 @@
};
// Generator function to create the indexOf and lastIndexOf functions
function createIndexFinder(dir, predicateFind, sortedIndex) {
var createIndexFinder = function(dir, predicateFind, sortedIndex) {
return function(array, item, idx) {
var i = 0, length = getLength(array);
if (typeof idx == 'number') {
if (dir > 0) {
i = idx >= 0 ? idx : Math.max(idx + length, i);
i = idx >= 0 ? idx : Math.max(idx + length, i);
} else {
length = idx >= 0 ? Math.min(idx + 1, length) : idx + length + 1;
length = idx >= 0 ? Math.min(idx + 1, length) : idx + length + 1;
}
} else if (sortedIndex && idx && length) {
idx = sortedIndex(array, item);
@@ -663,7 +668,7 @@
}
return -1;
};
}
};
// Return the position of the first occurrence of an item in an array,
// or -1 if the item is not included in the array.
@@ -692,6 +697,19 @@
return range;
};
// Split an **array** into several arrays containing **count** or less elements
// of initial array
_.chunk = function(array, count) {
if (count == null || count < 1) return [];
var result = [];
var i = 0, length = array.length;
while (i < length) {
result.push(slice.call(array, i, i += count));
}
return result;
};
// Function (ahem) Functions
// ------------------
@@ -708,45 +726,46 @@
// Create a function bound to a given object (assigning `this`, and arguments,
// optionally). Delegates to **ECMAScript 5**'s native `Function.bind` if
// available.
_.bind = function(func, context) {
if (nativeBind && func.bind === nativeBind) return nativeBind.apply(func, slice.call(arguments, 1));
_.bind = restArgs(function(func, context, args) {
if (!_.isFunction(func)) throw new TypeError('Bind must be called on a function');
var args = slice.call(arguments, 2);
var bound = function() {
return executeBound(func, bound, context, this, args.concat(slice.call(arguments)));
};
var bound = restArgs(function(callArgs) {
return executeBound(func, bound, context, this, args.concat(callArgs));
});
return bound;
};
});
// Partially apply a function by creating a version that has had some of its
// arguments pre-filled, without changing its dynamic `this` context. _ acts
// as a placeholder, allowing any combination of arguments to be pre-filled.
_.partial = function(func) {
var boundArgs = slice.call(arguments, 1);
// as a placeholder by default, allowing any combination of arguments to be
// pre-filled. Set `_.partial.placeholder` for a custom placeholder argument.
_.partial = restArgs(function(func, boundArgs) {
var placeholder = _.partial.placeholder;
var bound = function() {
var position = 0, length = boundArgs.length;
var args = Array(length);
for (var i = 0; i < length; i++) {
args[i] = boundArgs[i] === _ ? arguments[position++] : boundArgs[i];
args[i] = boundArgs[i] === placeholder ? arguments[position++] : boundArgs[i];
}
while (position < arguments.length) args.push(arguments[position++]);
return executeBound(func, bound, this, this, args);
};
return bound;
};
});
_.partial.placeholder = _;
// Bind a number of an object's methods to that object. Remaining arguments
// are the method names to be bound. Useful for ensuring that all callbacks
// defined on an object belong to it.
_.bindAll = function(obj) {
var i, length = arguments.length, key;
if (length <= 1) throw new Error('bindAll must be passed function names');
for (i = 1; i < length; i++) {
key = arguments[i];
_.bindAll = restArgs(function(obj, keys) {
keys = flatten(keys, false, false);
var index = keys.length;
if (index < 1) throw new Error('bindAll must be passed function names');
while (index--) {
var key = keys[index];
obj[key] = _.bind(obj[key], obj);
}
return obj;
};
});
// Memoize an expensive function by storing its results.
_.memoize = function(func, hasher) {
@@ -762,12 +781,11 @@
// Delays a function for the given number of milliseconds, and then calls
// it with the arguments supplied.
_.delay = function(func, wait) {
var args = slice.call(arguments, 2);
_.delay = restArgs(function(func, wait, args) {
return setTimeout(function(){
return func.apply(null, args);
}, wait);
};
});
// Defers a function, scheduling it to run after the current call stack has
// cleared.
@@ -898,6 +916,8 @@
// often you call it. Useful for lazy initialization.
_.once = _.partial(_.before, 2);
_.restArgs = restArgs;
// Object Functions
// ----------------
@@ -906,10 +926,10 @@
var nonEnumerableProps = ['valueOf', 'isPrototypeOf', 'toString',
'propertyIsEnumerable', 'hasOwnProperty', 'toLocaleString'];
function collectNonEnumProps(obj, keys) {
var collectNonEnumProps = function(obj, keys) {
var nonEnumIdx = nonEnumerableProps.length;
var constructor = obj.constructor;
var proto = (_.isFunction(constructor) && constructor.prototype) || ObjProto;
var proto = _.isFunction(constructor) && constructor.prototype || ObjProto;
// Constructor is a special case.
var prop = 'constructor';
@@ -921,7 +941,7 @@
keys.push(prop);
}
}
}
};
// Retrieve the names of an object's own properties.
// Delegates to **ECMAScript 5**'s native `Object.keys`
@@ -960,15 +980,14 @@
// In contrast to _.map it returns an object
_.mapObject = function(obj, iteratee, context) {
iteratee = cb(iteratee, context);
var keys = _.keys(obj),
length = keys.length,
results = {},
currentKey;
for (var index = 0; index < length; index++) {
currentKey = keys[index];
results[currentKey] = iteratee(obj[currentKey], currentKey, obj);
}
return results;
var keys = _.keys(obj),
length = keys.length,
results = {};
for (var index = 0; index < length; index++) {
var currentKey = keys[index];
results[currentKey] = iteratee(obj[currentKey], currentKey, obj);
}
return results;
};
// Convert an object into a list of `[key, value]` pairs.
@@ -1002,6 +1021,25 @@
return names.sort();
};
// An internal function for creating assigner functions.
var createAssigner = function(keysFunc, defaults) {
return function(obj) {
var length = arguments.length;
if (defaults) obj = Object(obj);
if (length < 2 || obj == null) return obj;
for (var index = 1; index < length; index++) {
var source = arguments[index],
keys = keysFunc(source),
l = keys.length;
for (var i = 0; i < l; i++) {
var key = keys[i];
if (!defaults || obj[key] === void 0) obj[key] = source[key];
}
}
return obj;
};
};
// Extend a given object with all the properties in passed-in object(s).
_.extend = createAssigner(_.allKeys);
@@ -1019,16 +1057,21 @@
}
};
// Internal pick helper function to determine if `obj` has key `key`.
var keyInObj = function(value, key, obj) {
return key in obj;
};
// Return a copy of the object only containing the whitelisted properties.
_.pick = function(object, oiteratee, context) {
var result = {}, obj = object, iteratee, keys;
_.pick = restArgs(function(obj, keys) {
var result = {}, iteratee = keys[0];
if (obj == null) return result;
if (_.isFunction(oiteratee)) {
if (_.isFunction(iteratee)) {
if (keys.length > 1) iteratee = optimizeCb(iteratee, keys[1]);
keys = _.allKeys(obj);
iteratee = optimizeCb(oiteratee, context);
} else {
keys = flatten(arguments, false, false, 1);
iteratee = function(value, key, obj) { return key in obj; };
iteratee = keyInObj;
keys = flatten(keys, false, false);
obj = Object(obj);
}
for (var i = 0, length = keys.length; i < length; i++) {
@@ -1037,20 +1080,22 @@
if (iteratee(value, key, obj)) result[key] = value;
}
return result;
};
});
// Return a copy of the object without the blacklisted properties.
_.omit = function(obj, iteratee, context) {
_.omit = restArgs(function(obj, keys) {
var iteratee = keys[0], context;
if (_.isFunction(iteratee)) {
iteratee = _.negate(iteratee);
if (keys.length > 1) context = keys[1];
} else {
var keys = _.map(flatten(arguments, false, false, 1), String);
keys = _.map(flatten(keys, false, false), String);
iteratee = function(value, key) {
return !_.contains(keys, key);
};
}
return _.pick(obj, iteratee, context);
};
});
// Fill in a given object with default properties.
_.defaults = createAssigner(_.allKeys, true);
@@ -1092,12 +1137,23 @@
// Internal recursive comparison function for `isEqual`.
var eq = function(a, b, aStack, bStack) {
var eq, deepEq;
eq = function(a, b, aStack, bStack) {
// Identical objects are equal. `0 === -0`, but they aren't identical.
// See the [Harmony `egal` proposal](http://wiki.ecmascript.org/doku.php?id=harmony:egal).
if (a === b) return a !== 0 || 1 / a === 1 / b;
// A strict comparison is necessary because `null == undefined`.
if (a == null || b == null) return a === b;
// `NaN`s are equivalent, but non-reflexive.
if (a !== a) return b !== b;
// Exhaust primitive checks
var type = typeof a;
if (type !== 'function' && type !== 'object' && typeof b !== 'object') return false;
return deepEq(a, b, aStack, bStack);
};
// Internal recursive comparison function for `isEqual`.
deepEq = function(a, b, aStack, bStack) {
// Unwrap any wrapped objects.
if (a instanceof _) a = a._wrapped;
if (b instanceof _) b = b._wrapped;
@@ -1230,8 +1286,9 @@
}
// Optimize `isFunction` if appropriate. Work around some typeof bugs in old v8,
// IE 11 (#1621), and in Safari 8 (#1929).
if (typeof /./ != 'function' && typeof Int8Array != 'object') {
// IE 11 (#1621), Safari 8 (#1929), and PhantomJS (#2236).
var nodelist = root.document && root.document.childNodes;
if (typeof /./ != 'function' && typeof Int8Array != 'object' && typeof nodelist != 'function') {
_.isFunction = function(obj) {
return typeof obj == 'function' || false;
};
@@ -1242,9 +1299,9 @@
return isFinite(obj) && !isNaN(parseFloat(obj));
};
// Is the given value `NaN`? (NaN is the only number which does not equal itself).
// Is the given value `NaN`?
_.isNaN = function(obj) {
return _.isNumber(obj) && obj !== +obj;
return _.isNumber(obj) && isNaN(obj);
};
// Is a given value a boolean?
@@ -1362,8 +1419,8 @@
// If the value of the named `property` is a function then invoke it with the
// `object` as context; otherwise, return it.
_.result = function(object, property, fallback) {
var value = object == null ? void 0 : object[property];
_.result = function(object, prop, fallback) {
var value = object == null ? void 0 : object[prop];
if (value === void 0) {
value = fallback;
}
@@ -1381,9 +1438,9 @@
// By default, Underscore uses ERB-style template delimiters, change the
// following template settings to use alternative delimiters.
_.templateSettings = {
evaluate : /<%([\s\S]+?)%>/g,
interpolate : /<%=([\s\S]+?)%>/g,
escape : /<%-([\s\S]+?)%>/g
evaluate: /<%([\s\S]+?)%>/g,
interpolate: /<%=([\s\S]+?)%>/g,
escape: /<%-([\s\S]+?)%>/g
};
// When customizing `templateSettings`, if you don't want to define an
@@ -1394,15 +1451,15 @@
// Certain characters need to be escaped so that they can be put into a
// string literal.
var escapes = {
"'": "'",
'\\': '\\',
'\r': 'r',
'\n': 'n',
"'": "'",
'\\': '\\',
'\r': 'r',
'\n': 'n',
'\u2028': 'u2028',
'\u2029': 'u2029'
};
var escaper = /\\|'|\r|\n|\u2028|\u2029/g;
var escapeRegExp = /\\|'|\r|\n|\u2028|\u2029/g;
var escapeChar = function(match) {
return '\\' + escapes[match];
@@ -1427,7 +1484,7 @@
var index = 0;
var source = "__p+='";
text.replace(matcher, function(match, escape, interpolate, evaluate, offset) {
source += text.slice(index, offset).replace(escaper, escapeChar);
source += text.slice(index, offset).replace(escapeRegExp, escapeChar);
index = offset + match.length;
if (escape) {
@@ -1438,7 +1495,7 @@
source += "';\n" + evaluate + "\n__p+='";
}
// Adobe VMs need the match returned to produce the correct offest.
// Adobe VMs need the match returned to produce the correct offset.
return match;
});
source += "';\n";
@@ -1450,8 +1507,9 @@
"print=function(){__p+=__j.call(arguments,'');};\n" +
source + 'return __p;\n';
var render;
try {
var render = new Function(settings.variable || 'obj', '_', source);
render = new Function(settings.variable || 'obj', '_', source);
} catch (e) {
e.source = source;
throw e;
@@ -1482,7 +1540,7 @@
// underscore functions. Wrapped objects may be chained.
// Helper function to continue chaining intermediate results.
var result = function(instance, obj) {
var chainResult = function(instance, obj) {
return instance._chain ? _(obj).chain() : obj;
};
@@ -1493,7 +1551,7 @@
_.prototype[name] = function() {
var args = [this._wrapped];
push.apply(args, arguments);
return result(this, func.apply(_, args));
return chainResult(this, func.apply(_, args));
};
});
};
@@ -1508,7 +1566,7 @@
var obj = this._wrapped;
method.apply(obj, arguments);
if ((name === 'shift' || name === 'splice') && obj.length === 0) delete obj[0];
return result(this, obj);
return chainResult(this, obj);
};
});
@@ -1516,7 +1574,7 @@
_.each(['concat', 'join', 'slice'], function(name) {
var method = ArrayProto[name];
_.prototype[name] = function() {
return result(this, method.apply(this._wrapped, arguments));
return chainResult(this, method.apply(this._wrapped, arguments));
};
});
@@ -1545,4 +1603,4 @@
return _;
});
}
}.call(this));
}());