From d85490fbbbeee82a806f0a665ba6a33afe1918a5 Mon Sep 17 00:00:00 2001 From: Jeremy Ashkenas Date: Thu, 5 Jan 2012 15:17:11 -0500 Subject: [PATCH 01/66] adding CNAME for underscorejs.org --- CNAME | 1 + 1 file changed, 1 insertion(+) create mode 100644 CNAME diff --git a/CNAME b/CNAME new file mode 100644 index 000000000..a007e65c4 --- /dev/null +++ b/CNAME @@ -0,0 +1 @@ +underscorejs.org From 908c0156312576433732f4a6f8db0ae229b9efdd Mon Sep 17 00:00:00 2001 From: Jeremy Ashkenas Date: Tue, 24 Jan 2012 10:33:14 -0500 Subject: [PATCH 02/66] Fixes #442 -- broken link to Objects/functions on the homepage --- index.html | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/index.html b/index.html index 52c967122..12d6958d1 100644 --- a/index.html +++ b/index.html @@ -211,7 +211,7 @@ @@ -1223,7 +1224,17 @@

Object Functions

_.isUndefined(window.missingVariable); => true - + +

+ isObject_.isObject(variable) +
+ Returns true if variable is an object. +

+
+_.isObject({});
+=> true
+
+

Utility Functions

From 53829cbd5c51e3319b843130890fbef0abd951c9 Mon Sep 17 00:00:00 2001 From: bjh Date: Thu, 26 Jan 2012 16:04:58 -0500 Subject: [PATCH 06/66] added a false case for the isObject test documentation --- index.html | 2 ++ 1 file changed, 2 insertions(+) diff --git a/index.html b/index.html index 3aa65a58a..b19f774c5 100644 --- a/index.html +++ b/index.html @@ -1233,6 +1233,8 @@

Object Functions

 _.isObject({});
 => true
+_.isObject('am I an object?');
+=> false
 

Utility Functions

From bdb3cb21f9aacf7de53169c99563b471f985b82d Mon Sep 17 00:00:00 2001 From: Jeremy Ashkenas Date: Mon, 30 Jan 2012 10:01:08 -0500 Subject: [PATCH 07/66] fixing isObject documentation. --- index.html | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/index.html b/index.html index b19f774c5..181e10983 100644 --- a/index.html +++ b/index.html @@ -221,6 +221,7 @@
  • - isEmpty
  • - isElement
  • - isArray
  • +
  • - isObject
  • - isArguments
  • - isFunction
  • - isString
  • @@ -231,7 +232,6 @@
  • - isNaN
  • - isNull
  • - isUndefined
  • -
  • - isObject
  • @@ -1113,6 +1113,18 @@

    Object Functions

    => false _.isArray([1,2,3]); => true + + +

    + isObject_.isObject(value) +
    + Returns true if value is an Object. +

    +
    +_.isObject({});
    +=> true
    +_.isObject(1);
    +=> false
     

    @@ -1223,18 +1235,6 @@

    Object Functions

     _.isUndefined(window.missingVariable);
     => true
    -
    - -

    - isObject_.isObject(variable) -
    - Returns true if variable is an object. -

    -
    -_.isObject({});
    -=> true
    -_.isObject('am I an object?');
    -=> false
     

    Utility Functions

    From 5c6abc4453f3bdf9470bd98ec79d7f22105f87e3 Mon Sep 17 00:00:00 2001 From: Brad Dunbar Date: Mon, 30 Jan 2012 10:15:04 -0500 Subject: [PATCH 08/66] short circuit `isEmpty` for null/undefined --- underscore.js | 1 + 1 file changed, 1 insertion(+) diff --git a/underscore.js b/underscore.js index a24750d19..c4e8c8a59 100644 --- a/underscore.js +++ b/underscore.js @@ -761,6 +761,7 @@ // Is a given array, string, or object empty? // An "empty" object has no enumerable own-properties. _.isEmpty = function(obj) { + if (obj == null) return true; if (_.isArray(obj) || _.isString(obj)) return obj.length === 0; for (var key in obj) if (_.has(obj, key)) return false; return true; From 458e604eee649aa0ee18cadfe96cd0e2b2739cb5 Mon Sep 17 00:00:00 2001 From: Brad Dunbar Date: Mon, 30 Jan 2012 10:30:06 -0500 Subject: [PATCH 09/66] upgrade qunit --- test/arrays.js | 120 +-- test/chaining.js | 6 +- test/collections.js | 108 +-- test/functions.js | 48 +- test/objects.js | 42 +- test/utility.js | 58 +- test/vendor/qunit.css | 54 +- test/vendor/qunit.js | 1905 ++++++++++++----------------------------- 8 files changed, 804 insertions(+), 1537 deletions(-) diff --git a/test/arrays.js b/test/arrays.js index b3b1ce15e..54cd9f8d2 100644 --- a/test/arrays.js +++ b/test/arrays.js @@ -3,69 +3,69 @@ $(document).ready(function() { module("Arrays"); test("arrays: first", function() { - equals(_.first([1,2,3]), 1, 'can pull out the first element of an array'); - equals(_([1, 2, 3]).first(), 1, 'can perform OO-style "first()"'); - equals(_.first([1,2,3], 0).join(', '), "", 'can pass an index to first'); - equals(_.first([1,2,3], 2).join(', '), '1, 2', 'can pass an index to first'); - equals(_.first([1,2,3], 5).join(', '), '1, 2, 3', 'can pass an index to first'); + equal(_.first([1,2,3]), 1, 'can pull out the first element of an array'); + equal(_([1, 2, 3]).first(), 1, 'can perform OO-style "first()"'); + equal(_.first([1,2,3], 0).join(', '), "", 'can pass an index to first'); + equal(_.first([1,2,3], 2).join(', '), '1, 2', 'can pass an index to first'); + equal(_.first([1,2,3], 5).join(', '), '1, 2, 3', 'can pass an index to first'); var result = (function(){ return _.first(arguments); })(4, 3, 2, 1); - equals(result, 4, 'works on an arguments object.'); + equal(result, 4, 'works on an arguments object.'); result = _.map([[1,2,3],[1,2,3]], _.first); - equals(result.join(','), '1,1', 'works well with _.map'); + equal(result.join(','), '1,1', 'works well with _.map'); }); test("arrays: rest", function() { var numbers = [1, 2, 3, 4]; - equals(_.rest(numbers).join(", "), "2, 3, 4", 'working rest()'); - equals(_.rest(numbers, 0).join(", "), "1, 2, 3, 4", 'working rest(0)'); - equals(_.rest(numbers, 2).join(', '), '3, 4', 'rest can take an index'); + equal(_.rest(numbers).join(", "), "2, 3, 4", 'working rest()'); + equal(_.rest(numbers, 0).join(", "), "1, 2, 3, 4", 'working rest(0)'); + equal(_.rest(numbers, 2).join(', '), '3, 4', 'rest can take an index'); var result = (function(){ return _(arguments).tail(); })(1, 2, 3, 4); - equals(result.join(', '), '2, 3, 4', 'aliased as tail and works on arguments object'); + equal(result.join(', '), '2, 3, 4', 'aliased as tail and works on arguments object'); result = _.map([[1,2,3],[1,2,3]], _.rest); - equals(_.flatten(result).join(','), '2,3,2,3', 'works well with _.map'); + equal(_.flatten(result).join(','), '2,3,2,3', 'works well with _.map'); }); test("arrays: initial", function() { - equals(_.initial([1,2,3,4,5]).join(", "), "1, 2, 3, 4", 'working initial()'); - equals(_.initial([1,2,3,4],2).join(", "), "1, 2", 'initial can take an index'); + equal(_.initial([1,2,3,4,5]).join(", "), "1, 2, 3, 4", 'working initial()'); + equal(_.initial([1,2,3,4],2).join(", "), "1, 2", 'initial can take an index'); var result = (function(){ return _(arguments).initial(); })(1, 2, 3, 4); - equals(result.join(", "), "1, 2, 3", 'initial works on arguments object'); + equal(result.join(", "), "1, 2, 3", 'initial works on arguments object'); result = _.map([[1,2,3],[1,2,3]], _.initial); - equals(_.flatten(result).join(','), '1,2,1,2', 'initial works with _.map'); + equal(_.flatten(result).join(','), '1,2,1,2', 'initial works with _.map'); }); test("arrays: last", function() { - equals(_.last([1,2,3]), 3, 'can pull out the last element of an array'); - equals(_.last([1,2,3], 0).join(', '), "", 'can pass an index to last'); - equals(_.last([1,2,3], 2).join(', '), '2, 3', 'can pass an index to last'); - equals(_.last([1,2,3], 5).join(', '), '1, 2, 3', 'can pass an index to last'); + equal(_.last([1,2,3]), 3, 'can pull out the last element of an array'); + equal(_.last([1,2,3], 0).join(', '), "", 'can pass an index to last'); + equal(_.last([1,2,3], 2).join(', '), '2, 3', 'can pass an index to last'); + equal(_.last([1,2,3], 5).join(', '), '1, 2, 3', 'can pass an index to last'); var result = (function(){ return _(arguments).last(); })(1, 2, 3, 4); - equals(result, 4, 'works on an arguments object'); + equal(result, 4, 'works on an arguments object'); result = _.map([[1,2,3],[1,2,3]], _.last); - equals(result.join(','), '3,3', 'works well with _.map'); + equal(result.join(','), '3,3', 'works well with _.map'); }); test("arrays: compact", function() { - equals(_.compact([0, 1, false, 2, false, 3]).length, 3, 'can trim out all falsy values'); + equal(_.compact([0, 1, false, 2, false, 3]).length, 3, 'can trim out all falsy values'); var result = (function(){ return _(arguments).compact().length; })(0, 1, false, 2, false, 3); - equals(result, 3, 'works on an arguments object'); + equal(result, 3, 'works on an arguments object'); }); test("arrays: flatten", function() { if (window.JSON) { var list = [1, [2], [3, [[[4]]]]]; - equals(JSON.stringify(_.flatten(list)), '[1,2,3,4]', 'can flatten nested arrays'); - equals(JSON.stringify(_.flatten(list, true)), '[1,2,3,[[[4]]]]', 'can shallowly flatten nested arrays'); + equal(JSON.stringify(_.flatten(list)), '[1,2,3,4]', 'can flatten nested arrays'); + equal(JSON.stringify(_.flatten(list, true)), '[1,2,3,[[[4]]]]', 'can shallowly flatten nested arrays'); var result = (function(){ return _.flatten(arguments); })(1, [2], [3, [[[4]]]]); - equals(JSON.stringify(result), '[1,2,3,4]', 'works on an arguments object'); + equal(JSON.stringify(result), '[1,2,3,4]', 'works on an arguments object'); } }); test("arrays: without", function() { var list = [1, 2, 1, 0, 3, 1, 4]; - equals(_.without(list, 0, 1).join(', '), '2, 3, 4', 'can remove all instances of an object'); + equal(_.without(list, 0, 1).join(', '), '2, 3, 4', 'can remove all instances of an object'); var result = (function(){ return _.without(arguments, 0, 1); })(1, 2, 1, 0, 3, 1, 4); - equals(result.join(', '), '2, 3, 4', 'works on an arguments object'); + equal(result.join(', '), '2, 3, 4', 'works on an arguments object'); var list = [{one : 1}, {two : 2}]; ok(_.without(list, {one : 1}).length == 2, 'uses real object identity for comparisons.'); @@ -74,93 +74,93 @@ $(document).ready(function() { test("arrays: uniq", function() { var list = [1, 2, 1, 3, 1, 4]; - equals(_.uniq(list).join(', '), '1, 2, 3, 4', 'can find the unique values of an unsorted array'); + equal(_.uniq(list).join(', '), '1, 2, 3, 4', 'can find the unique values of an unsorted array'); var list = [1, 1, 1, 2, 2, 3]; - equals(_.uniq(list, true).join(', '), '1, 2, 3', 'can find the unique values of a sorted array faster'); + equal(_.uniq(list, true).join(', '), '1, 2, 3', 'can find the unique values of a sorted array faster'); var list = [{name:'moe'}, {name:'curly'}, {name:'larry'}, {name:'curly'}]; var iterator = function(value) { return value.name; }; - equals(_.map(_.uniq(list, false, iterator), iterator).join(', '), 'moe, curly, larry', 'can find the unique values of an array using a custom iterator'); + equal(_.map(_.uniq(list, false, iterator), iterator).join(', '), 'moe, curly, larry', 'can find the unique values of an array using a custom iterator'); var iterator = function(value) { return value +1; }; var list = [1, 2, 2, 3, 4, 4]; - equals(_.uniq(list, true, iterator).join(', '), '1, 2, 3, 4', 'iterator works with sorted array'); + equal(_.uniq(list, true, iterator).join(', '), '1, 2, 3, 4', 'iterator works with sorted array'); var result = (function(){ return _.uniq(arguments); })(1, 2, 1, 3, 1, 4); - equals(result.join(', '), '1, 2, 3, 4', 'works on an arguments object'); + equal(result.join(', '), '1, 2, 3, 4', 'works on an arguments object'); }); test("arrays: intersection", function() { var stooges = ['moe', 'curly', 'larry'], leaders = ['moe', 'groucho']; - equals(_.intersection(stooges, leaders).join(''), 'moe', 'can take the set intersection of two arrays'); - equals(_(stooges).intersection(leaders).join(''), 'moe', 'can perform an OO-style intersection'); + equal(_.intersection(stooges, leaders).join(''), 'moe', 'can take the set intersection of two arrays'); + equal(_(stooges).intersection(leaders).join(''), 'moe', 'can perform an OO-style intersection'); var result = (function(){ return _.intersection(arguments, leaders); })('moe', 'curly', 'larry'); - equals(result.join(''), 'moe', 'works on an arguments object'); + equal(result.join(''), 'moe', 'works on an arguments object'); }); test("arrays: union", function() { var result = _.union([1, 2, 3], [2, 30, 1], [1, 40]); - equals(result.join(' '), '1 2 3 30 40', 'takes the union of a list of arrays'); + equal(result.join(' '), '1 2 3 30 40', 'takes the union of a list of arrays'); var result = _.union([1, 2, 3], [2, 30, 1], [1, 40, [1]]); - equals(result.join(' '), '1 2 3 30 40 1', 'takes the union of a list of nested arrays'); + equal(result.join(' '), '1 2 3 30 40 1', 'takes the union of a list of nested arrays'); }); test("arrays: difference", function() { var result = _.difference([1, 2, 3], [2, 30, 40]); - equals(result.join(' '), '1 3', 'takes the difference of two arrays'); + equal(result.join(' '), '1 3', 'takes the difference of two arrays'); var result = _.difference([1, 2, 3, 4], [2, 30, 40], [1, 11, 111]); - equals(result.join(' '), '3 4', 'takes the difference of three arrays'); + equal(result.join(' '), '3 4', 'takes the difference of three arrays'); }); test('arrays: zip', function() { var names = ['moe', 'larry', 'curly'], ages = [30, 40, 50], leaders = [true]; var stooges = _.zip(names, ages, leaders); - equals(String(stooges), 'moe,30,true,larry,40,,curly,50,', 'zipped together arrays of different lengths'); + equal(String(stooges), 'moe,30,true,larry,40,,curly,50,', 'zipped together arrays of different lengths'); }); test("arrays: indexOf", function() { var numbers = [1, 2, 3]; numbers.indexOf = null; - equals(_.indexOf(numbers, 2), 1, 'can compute indexOf, even without the native function'); + equal(_.indexOf(numbers, 2), 1, 'can compute indexOf, even without the native function'); var result = (function(){ return _.indexOf(arguments, 2); })(1, 2, 3); - equals(result, 1, 'works on an arguments object'); - equals(_.indexOf(null, 2), -1, 'handles nulls properly'); + equal(result, 1, 'works on an arguments object'); + equal(_.indexOf(null, 2), -1, 'handles nulls properly'); var numbers = [10, 20, 30, 40, 50], num = 35; var index = _.indexOf(numbers, num, true); - equals(index, -1, '35 is not in the list'); + equal(index, -1, '35 is not in the list'); numbers = [10, 20, 30, 40, 50]; num = 40; index = _.indexOf(numbers, num, true); - equals(index, 3, '40 is in the list'); + equal(index, 3, '40 is in the list'); numbers = [1, 40, 40, 40, 40, 40, 40, 40, 50, 60, 70]; num = 40; index = _.indexOf(numbers, num, true); - equals(index, 1, '40 is in the list'); + equal(index, 1, '40 is in the list'); }); test("arrays: lastIndexOf", function() { var numbers = [1, 0, 1, 0, 0, 1, 0, 0, 0]; numbers.lastIndexOf = null; - equals(_.lastIndexOf(numbers, 1), 5, 'can compute lastIndexOf, even without the native function'); - equals(_.lastIndexOf(numbers, 0), 8, 'lastIndexOf the other element'); + equal(_.lastIndexOf(numbers, 1), 5, 'can compute lastIndexOf, even without the native function'); + equal(_.lastIndexOf(numbers, 0), 8, 'lastIndexOf the other element'); var result = (function(){ return _.lastIndexOf(arguments, 1); })(1, 0, 1, 0, 0, 1, 0, 0, 0); - equals(result, 5, 'works on an arguments object'); - equals(_.indexOf(null, 2), -1, 'handles nulls properly'); + equal(result, 5, 'works on an arguments object'); + equal(_.indexOf(null, 2), -1, 'handles nulls properly'); }); test("arrays: range", function() { - equals(_.range(0).join(''), '', 'range with 0 as a first argument generates an empty array'); - equals(_.range(4).join(' '), '0 1 2 3', 'range with a single positive argument generates an array of elements 0,1,2,...,n-1'); - equals(_.range(5, 8).join(' '), '5 6 7', 'range with two arguments a & b, a<b generates an array of elements a,a+1,a+2,...,b-2,b-1'); - equals(_.range(8, 5).join(''), '', 'range with two arguments a & b, b<a generates an empty array'); - equals(_.range(3, 10, 3).join(' '), '3 6 9', 'range with three arguments a & b & c, c < b-a, a < b generates an array of elements a,a+c,a+2c,...,b - (multiplier of a) < c'); - equals(_.range(3, 10, 15).join(''), '3', 'range with three arguments a & b & c, c > b-a, a < b generates an array with a single element, equal to a'); - equals(_.range(12, 7, -2).join(' '), '12 10 8', 'range with three arguments a & b & c, a > b, c < 0 generates an array of elements a,a-c,a-2c and ends with the number not less than b'); - equals(_.range(0, -10, -1).join(' '), '0 -1 -2 -3 -4 -5 -6 -7 -8 -9', 'final example in the Python docs'); + equal(_.range(0).join(''), '', 'range with 0 as a first argument generates an empty array'); + equal(_.range(4).join(' '), '0 1 2 3', 'range with a single positive argument generates an array of elements 0,1,2,...,n-1'); + equal(_.range(5, 8).join(' '), '5 6 7', 'range with two arguments a & b, a<b generates an array of elements a,a+1,a+2,...,b-2,b-1'); + equal(_.range(8, 5).join(''), '', 'range with two arguments a & b, b<a generates an empty array'); + equal(_.range(3, 10, 3).join(' '), '3 6 9', 'range with three arguments a & b & c, c < b-a, a < b generates an array of elements a,a+c,a+2c,...,b - (multiplier of a) < c'); + equal(_.range(3, 10, 15).join(''), '3', 'range with three arguments a & b & c, c > b-a, a < b generates an array with a single element, equal to a'); + equal(_.range(12, 7, -2).join(' '), '12 10 8', 'range with three arguments a & b & c, a > b, c < 0 generates an array of elements a,a-c,a-2c and ends with the number not less than b'); + equal(_.range(0, -10, -1).join(' '), '0 -1 -2 -3 -4 -5 -6 -7 -8 -9', 'final example in the Python docs'); }); }); diff --git a/test/chaining.js b/test/chaining.js index 0e3d5f385..257587224 100644 --- a/test/chaining.js +++ b/test/chaining.js @@ -29,7 +29,7 @@ $(document).ready(function() { }).sortBy(function(n) { return -n; }).value(); - equals(numbers.join(', '), "10, 6, 2", "filtered and reversed the numbers"); + equal(numbers.join(', '), "10, 6, 2", "filtered and reversed the numbers"); }); test("chaining: select/reject/sortBy in functional style", function() { @@ -41,7 +41,7 @@ $(document).ready(function() { }).sortBy(function(n) { return -n; }).value(); - equals(numbers.join(', '), "10, 6, 2", "filtered and reversed the numbers"); + equal(numbers.join(', '), "10, 6, 2", "filtered and reversed the numbers"); }); test("chaining: reverse/concat/unshift/pop/map", function() { @@ -53,7 +53,7 @@ $(document).ready(function() { .pop() .map(function(n){ return n * 2; }) .value(); - equals(numbers.join(', '), "34, 10, 8, 6, 4, 2, 10, 10", 'can chain together array functions.'); + equal(numbers.join(', '), "34, 10, 8, 6, 4, 2, 10, 10", 'can chain together array functions.'); }); }); diff --git a/test/collections.js b/test/collections.js index 838c0cf4d..3f623b4d9 100644 --- a/test/collections.js +++ b/test/collections.js @@ -4,22 +4,22 @@ $(document).ready(function() { test("collections: each", function() { _.each([1, 2, 3], function(num, i) { - equals(num, i + 1, 'each iterators provide value and iteration count'); + equal(num, i + 1, 'each iterators provide value and iteration count'); }); var answers = []; _.each([1, 2, 3], function(num){ answers.push(num * this.multiplier);}, {multiplier : 5}); - equals(answers.join(', '), '5, 10, 15', 'context object property accessed'); + equal(answers.join(', '), '5, 10, 15', 'context object property accessed'); answers = []; _.forEach([1, 2, 3], function(num){ answers.push(num); }); - equals(answers.join(', '), '1, 2, 3', 'aliased as "forEach"'); + equal(answers.join(', '), '1, 2, 3', 'aliased as "forEach"'); answers = []; var obj = {one : 1, two : 2, three : 3}; obj.constructor.prototype.four = 4; _.each(obj, function(value, key){ answers.push(key); }); - equals(answers.join(", "), 'one, two, three', 'iterating over objects works, and ignores the object prototype.'); + equal(answers.join(", "), 'one, two, three', 'iterating over objects works, and ignores the object prototype.'); delete obj.constructor.prototype.four; answer = null; @@ -28,21 +28,21 @@ $(document).ready(function() { answers = 0; _.each(null, function(){ ++answers; }); - equals(answers, 0, 'handles a null properly'); + equal(answers, 0, 'handles a null properly'); }); test('collections: map', function() { var doubled = _.map([1, 2, 3], function(num){ return num * 2; }); - equals(doubled.join(', '), '2, 4, 6', 'doubled numbers'); + equal(doubled.join(', '), '2, 4, 6', 'doubled numbers'); doubled = _.collect([1, 2, 3], function(num){ return num * 2; }); - equals(doubled.join(', '), '2, 4, 6', 'aliased as "collect"'); + equal(doubled.join(', '), '2, 4, 6', 'aliased as "collect"'); var tripled = _.map([1, 2, 3], function(num){ return num * this.multiplier; }, {multiplier : 3}); - equals(tripled.join(', '), '3, 6, 9', 'tripled numbers with context'); + equal(tripled.join(', '), '3, 6, 9', 'tripled numbers with context'); var doubled = _([1, 2, 3]).map(function(num){ return num * 2; }); - equals(doubled.join(', '), '2, 4, 6', 'OO-style doubled numbers'); + equal(doubled.join(', '), '2, 4, 6', 'OO-style doubled numbers'); var ids = _.map($('div.underscore-test').children(), function(n){ return n.id; }); ok(_.include(ids, 'qunit-header'), 'can use collection methods on NodeLists'); @@ -54,25 +54,25 @@ $(document).ready(function() { ok(_.isArray(ifnull) && ifnull.length === 0, 'handles a null properly'); var length = _.map(Array(2), function(v) { return v; }).length; - equals(length, 2, "can preserve a sparse array's length"); + equal(length, 2, "can preserve a sparse array's length"); }); test('collections: reduce', function() { var sum = _.reduce([1, 2, 3], function(sum, num){ return sum + num; }, 0); - equals(sum, 6, 'can sum up an array'); + 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); - equals(sum, 18, 'can reduce with a context object'); + equal(sum, 18, 'can reduce with a context object'); sum = _.inject([1, 2, 3], function(sum, num){ return sum + num; }, 0); - equals(sum, 6, 'aliased as "inject"'); + equal(sum, 6, 'aliased as "inject"'); sum = _([1, 2, 3]).reduce(function(sum, num){ return sum + num; }, 0); - equals(sum, 6, 'OO-style reduce'); + equal(sum, 6, 'OO-style reduce'); var sum = _.reduce([1, 2, 3], function(sum, num){ return sum + num; }); - equals(sum, 6, 'default initial value'); + equal(sum, 6, 'default initial value'); var ifnull; try { @@ -83,24 +83,24 @@ $(document).ready(function() { ok(ifnull instanceof TypeError, 'handles a null (without inital value) properly'); ok(_.reduce(null, function(){}, 138) === 138, 'handles a null (with initial value) properly'); - equals(_.reduce([], function(){}, undefined), undefined, 'undefined can be passed as a special case'); + equal(_.reduce([], function(){}, undefined), undefined, 'undefined can be passed as a special case'); raises(function() { _.reduce([], function(){}); }, TypeError, 'throws an error for empty arrays with no initial value'); var sparseArray = []; sparseArray[0] = 20; sparseArray[2] = -5; - equals(_.reduce(sparseArray, function(a, b){ return a - b; }), 25, 'initially-sparse arrays with no memo'); + equal(_.reduce(sparseArray, function(a, b){ return a - b; }), 25, 'initially-sparse arrays with no memo'); }); test('collections: reduceRight', function() { var list = _.reduceRight(["foo", "bar", "baz"], function(memo, str){ return memo + str; }, ''); - equals(list, 'bazbarfoo', 'can perform right folds'); + equal(list, 'bazbarfoo', 'can perform right folds'); var list = _.foldr(["foo", "bar", "baz"], function(memo, str){ return memo + str; }, ''); - equals(list, 'bazbarfoo', 'aliased as "foldr"'); + equal(list, 'bazbarfoo', 'aliased as "foldr"'); var list = _.foldr(["foo", "bar", "baz"], function(memo, str){ return memo + str; }); - equals(list, 'bazbarfoo', 'default initial value'); + equal(list, 'bazbarfoo', 'default initial value'); var ifnull; try { @@ -112,31 +112,31 @@ $(document).ready(function() { ok(_.reduceRight(null, function(){}, 138) === 138, 'handles a null (with initial value) properly'); - equals(_.reduceRight([], function(){}, undefined), undefined, 'undefined can be passed as a special case'); + equal(_.reduceRight([], function(){}, undefined), undefined, 'undefined can be passed as a special case'); raises(function() { _.reduceRight([], function(){}); }, TypeError, 'throws an error for empty arrays with no initial value'); var sparseArray = []; sparseArray[0] = 20; sparseArray[2] = -5; - equals(_.reduceRight(sparseArray, function(a, b){ return a - b; }), -25, 'initially-sparse arrays with no memo'); + equal(_.reduceRight(sparseArray, function(a, b){ return a - b; }), -25, 'initially-sparse arrays with no memo'); }); test('collections: detect', function() { var result = _.detect([1, 2, 3], function(num){ return num * 2 == 4; }); - equals(result, 2, 'found the first "2" and broke the loop'); + equal(result, 2, 'found the first "2" and broke the loop'); }); test('collections: select', function() { var evens = _.select([1, 2, 3, 4, 5, 6], function(num){ return num % 2 == 0; }); - equals(evens.join(', '), '2, 4, 6', 'selected each even number'); + equal(evens.join(', '), '2, 4, 6', 'selected each even number'); evens = _.filter([1, 2, 3, 4, 5, 6], function(num){ return num % 2 == 0; }); - equals(evens.join(', '), '2, 4, 6', 'aliased as "filter"'); + equal(evens.join(', '), '2, 4, 6', 'aliased as "filter"'); }); test('collections: reject', function() { var odds = _.reject([1, 2, 3, 4, 5, 6], function(num){ return num % 2 == 0; }); - equals(odds.join(', '), '1, 3, 5', 'rejected each even number'); + equal(odds.join(', '), '1, 3, 5', 'rejected each even number'); }); test('collections: all', function() { @@ -172,15 +172,15 @@ $(document).ready(function() { test('collections: invoke', function() { var list = [[5, 1, 7], [3, 2, 1]]; var result = _.invoke(list, 'sort'); - equals(result[0].join(', '), '1, 5, 7', 'first array sorted'); - equals(result[1].join(', '), '1, 2, 3', 'second array sorted'); + equal(result[0].join(', '), '1, 5, 7', 'first array sorted'); + equal(result[1].join(', '), '1, 2, 3', 'second array sorted'); }); test('collections: invoke w/ function reference', function() { var list = [[5, 1, 7], [3, 2, 1]]; var result = _.invoke(list, Array.prototype.sort); - equals(result[0].join(', '), '1, 5, 7', 'first array sorted'); - equals(result[1].join(', '), '1, 2, 3', 'second array sorted'); + equal(result[0].join(', '), '1, 5, 7', 'first array sorted'); + equal(result[1].join(', '), '1, 2, 3', 'second array sorted'); }); // Relevant when using ClojureScript @@ -190,72 +190,72 @@ $(document).ready(function() { }; var list = [[5, 1, 7], [3, 2, 1]]; var s = "foo"; - equals(s.call(), 42, "call function exists"); + equal(s.call(), 42, "call function exists"); var result = _.invoke(list, 'sort'); - equals(result[0].join(', '), '1, 5, 7', 'first array sorted'); - equals(result[1].join(', '), '1, 2, 3', 'second array sorted'); + equal(result[0].join(', '), '1, 5, 7', 'first array sorted'); + equal(result[1].join(', '), '1, 2, 3', 'second array sorted'); delete String.prototype.call; - equals(s.call, undefined, "call function removed"); + equal(s.call, undefined, "call function removed"); }); test('collections: pluck', function() { var people = [{name : 'moe', age : 30}, {name : 'curly', age : 50}]; - equals(_.pluck(people, 'name').join(', '), 'moe, curly', 'pulls names out of objects'); + equal(_.pluck(people, 'name').join(', '), 'moe, curly', 'pulls names out of objects'); }); test('collections: max', function() { - equals(3, _.max([1, 2, 3]), 'can perform a regular Math.max'); + equal(3, _.max([1, 2, 3]), 'can perform a regular Math.max'); var neg = _.max([1, 2, 3], function(num){ return -num; }); - equals(neg, 1, 'can perform a computation-based max'); + equal(neg, 1, 'can perform a computation-based max'); - equals(-Infinity, _.max({}), 'Maximum value of an empty object'); - equals(-Infinity, _.max([]), 'Maximum value of an empty array'); + equal(-Infinity, _.max({}), 'Maximum value of an empty object'); + equal(-Infinity, _.max([]), 'Maximum value of an empty array'); }); test('collections: min', function() { - equals(1, _.min([1, 2, 3]), 'can perform a regular Math.min'); + equal(1, _.min([1, 2, 3]), 'can perform a regular Math.min'); var neg = _.min([1, 2, 3], function(num){ return -num; }); - equals(neg, 3, 'can perform a computation-based min'); + equal(neg, 3, 'can perform a computation-based min'); - equals(Infinity, _.min({}), 'Minimum value of an empty object'); - equals(Infinity, _.min([]), 'Minimum value of an empty array'); + equal(Infinity, _.min({}), 'Minimum value of an empty object'); + equal(Infinity, _.min([]), 'Minimum value of an empty array'); var now = new Date(9999999999); var then = new Date(0); - equals(_.min([now, then]), then); + equal(_.min([now, then]), then); }); test('collections: sortBy', function() { var people = [{name : 'curly', age : 50}, {name : 'moe', age : 30}]; people = _.sortBy(people, function(person){ return person.age; }); - equals(_.pluck(people, 'name').join(', '), 'moe, curly', 'stooges sorted by age'); + equal(_.pluck(people, 'name').join(', '), 'moe, curly', 'stooges sorted by age'); }); test('collections: groupBy', function() { var parity = _.groupBy([1, 2, 3, 4, 5, 6], function(num){ return num % 2; }); ok('0' in parity && '1' in parity, 'created a group for each value'); - equals(parity[0].join(', '), '2, 4, 6', 'put each even number in the right group'); + equal(parity[0].join(', '), '2, 4, 6', 'put each even number in the right group'); var list = ["one", "two", "three", "four", "five", "six", "seven", "eight", "nine", "ten"]; var grouped = _.groupBy(list, 'length'); - equals(grouped['3'].join(' '), 'one two six ten'); - equals(grouped['4'].join(' '), 'four five nine'); - equals(grouped['5'].join(' '), 'three seven eight'); + equal(grouped['3'].join(' '), 'one two six ten'); + equal(grouped['4'].join(' '), 'four five nine'); + equal(grouped['5'].join(' '), 'three seven eight'); }); test('collections: sortedIndex', function() { var numbers = [10, 20, 30, 40, 50], num = 35; var index = _.sortedIndex(numbers, num); - equals(index, 3, '35 should be inserted at index 3'); + equal(index, 3, '35 should be inserted at index 3'); }); test('collections: shuffle', function() { var numbers = _.range(10); var shuffled = _.shuffle(numbers).sort(); notStrictEqual(numbers, shuffled, 'original object is unmodified'); - equals(shuffled.join(','), numbers.join(','), 'contains the same members before and after shuffle'); + equal(shuffled.join(','), numbers.join(','), 'contains the same members before and after shuffle'); }); test('collections: toArray', function() { @@ -263,14 +263,14 @@ $(document).ready(function() { ok(_.isArray(_.toArray(arguments)), 'arguments object converted into array'); var a = [1,2,3]; ok(_.toArray(a) !== a, 'array is cloned'); - equals(_.toArray(a).join(', '), '1, 2, 3', 'cloned array contains same elements'); + equal(_.toArray(a).join(', '), '1, 2, 3', 'cloned array contains same elements'); var numbers = _.toArray({one : 1, two : 2, three : 3}); - equals(numbers.join(', '), '1, 2, 3', 'object flattened into array'); + equal(numbers.join(', '), '1, 2, 3', 'object flattened into array'); }); test('collections: size', function() { - equals(_.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'); }); }); diff --git a/test/functions.js b/test/functions.js index 78721af16..e6c402a18 100644 --- a/test/functions.js +++ b/test/functions.js @@ -6,26 +6,26 @@ $(document).ready(function() { var context = {name : 'moe'}; var func = function(arg) { return "name: " + (this.name || arg); }; var bound = _.bind(func, context); - equals(bound(), 'name: moe', 'can bind a function to a context'); + equal(bound(), 'name: moe', 'can bind a function to a context'); bound = _(func).bind(context); - equals(bound(), 'name: moe', 'can do OO-style binding'); + equal(bound(), 'name: moe', 'can do OO-style binding'); bound = _.bind(func, null, 'curly'); - equals(bound(), 'name: curly', 'can bind without specifying a context'); + equal(bound(), 'name: curly', 'can bind without specifying a context'); func = function(salutation, name) { return salutation + ': ' + name; }; func = _.bind(func, this, 'hello'); - equals(func('moe'), 'hello: moe', 'the function was partially applied in advance'); + equal(func('moe'), 'hello: moe', 'the function was partially applied in advance'); var func = _.bind(func, this, 'curly'); - equals(func(), 'hello: curly', 'the function was completely applied in advance'); + equal(func(), 'hello: curly', 'the function was completely applied in advance'); var func = function(salutation, firstname, lastname) { return salutation + ': ' + firstname + ' ' + lastname; }; func = _.bind(func, this, 'hello', 'moe', 'curly'); - equals(func(), 'hello: moe curly', 'the function was partially applied in advance and can accept multiple arguments'); + equal(func(), 'hello: moe curly', 'the function was partially applied in advance and can accept multiple arguments'); - func = function(context, message) { equals(this, context, message); }; + func = function(context, message) { equal(this, context, 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`')(); @@ -47,8 +47,8 @@ $(document).ready(function() { curly.getName = moe.getName; _.bindAll(moe, 'getName', 'sayHi'); curly.sayHi = moe.sayHi; - equals(curly.getName(), 'name: curly', 'unbound function is bound to current object'); - equals(curly.sayHi(), 'hi: moe', 'bound function is still bound to original object'); + 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'}; moe = { @@ -58,7 +58,7 @@ $(document).ready(function() { }; _.bindAll(moe); curly.sayHi = moe.sayHi; - equals(curly.sayHi(), 'hi: moe', 'calling bindAll with no arguments binds all functions to the object'); + equal(curly.sayHi(), 'hi: moe', 'calling bindAll with no arguments binds all functions to the object'); }); test("functions: memoize", function() { @@ -66,15 +66,15 @@ $(document).ready(function() { return n < 2 ? n : fib(n - 1) + fib(n - 2); }; var fastFib = _.memoize(fib); - equals(fib(10), 55, 'a memoized version of fibonacci produces identical results'); - equals(fastFib(10), 55, 'a memoized version of fibonacci produces identical results'); + equal(fib(10), 55, 'a memoized version of fibonacci produces identical results'); + equal(fastFib(10), 55, 'a memoized version of fibonacci produces identical results'); var o = function(str) { return str; }; var fastO = _.memoize(o); - equals(o('toString'), 'toString', 'checks hasOwnProperty'); - equals(fastO('toString'), 'toString', 'checks hasOwnProperty'); + equal(o('toString'), 'toString', 'checks hasOwnProperty'); + equal(fastO('toString'), 'toString', 'checks hasOwnProperty'); }); asyncTest("functions: delay", 2, function() { @@ -113,8 +113,8 @@ $(document).ready(function() { setTimeout(function(){ throttledUpdate(4); }, 120); setTimeout(function(){ throttledUpdate(5); }, 140); setTimeout(function(){ throttledUpdate(6); }, 250); - _.delay(function(){ equals(value, 1, "updated to latest value"); }, 40); - _.delay(function(){ equals(value, 6, "updated to latest value"); start(); }, 400); + _.delay(function(){ equal(value, 1, "updated to latest value"); }, 40); + _.delay(function(){ equal(value, 6, "updated to latest value"); start(); }, 400); }); asyncTest("functions: throttle once", 1, function() { @@ -151,18 +151,18 @@ $(document).ready(function() { var increment = _.once(function(){ num++; }); increment(); increment(); - equals(num, 1); + equal(num, 1); }); test("functions: wrap", function() { var greet = function(name){ return "hi: " + name; }; var backwards = _.wrap(greet, function(func, name){ return func(name) + ' ' + name.split('').reverse().join(''); }); - equals(backwards('moe'), 'hi: moe eom', 'wrapped the saluation function'); + equal(backwards('moe'), 'hi: moe eom', 'wrapped the saluation function'); var inner = function(){ return "Hello "; }; var obj = {name : "Moe"}; obj.hi = _.wrap(inner, function(fn){ return fn() + this.name; }); - equals(obj.hi(), "Hello Moe"); + equal(obj.hi(), "Hello Moe"); var noop = function(){}; var wrapped = _.wrap(noop, function(fn){ return Array.prototype.slice.call(arguments, 0); }); @@ -174,10 +174,10 @@ $(document).ready(function() { var greet = function(name){ return "hi: " + name; }; var exclaim = function(sentence){ return sentence + '!'; }; var composed = _.compose(exclaim, greet); - equals(composed('moe'), 'hi: moe!', 'can compose a function that takes another'); + equal(composed('moe'), 'hi: moe!', 'can compose a function that takes another'); composed = _.compose(greet, exclaim); - equals(composed('moe'), 'hi: moe!', 'in this case, the functions are also commutative'); + equal(composed('moe'), 'hi: moe!', 'in this case, the functions are also commutative'); }); test("functions: after", function() { @@ -190,9 +190,9 @@ $(document).ready(function() { return afterCalled; }; - equals(testAfter(5, 5), 1, "after(N) should fire after being called N times"); - equals(testAfter(5, 4), 0, "after(N) should not fire unless called N times"); - equals(testAfter(0, 0), 1, "after(0) should fire immediately"); + equal(testAfter(5, 5), 1, "after(N) should fire after being called N times"); + equal(testAfter(5, 4), 0, "after(N) should not fire unless called N times"); + equal(testAfter(0, 0), 1, "after(0) should fire immediately"); }); }); diff --git a/test/objects.js b/test/objects.js index 0105d608a..1745d8a3f 100644 --- a/test/objects.js +++ b/test/objects.js @@ -4,10 +4,10 @@ $(document).ready(function() { test("objects: keys", function() { var exception = /object/; - equals(_.keys({one : 1, two : 2}).join(', '), 'one, two', 'can extract the keys from an object'); + equal(_.keys({one : 1, two : 2}).join(', '), '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; - equals(_.keys(a).join(', '), '1', 'is not fooled by sparse arrays; see issue #95'); + equal(_.keys(a).join(', '), '1', 'is not fooled by sparse arrays; see issue #95'); raises(function() { _.keys(null); }, exception, 'throws an error for `null` values'); raises(function() { _.keys(void 0); }, exception, 'throws an error for `undefined` values'); raises(function() { _.keys(1); }, exception, 'throws an error for number primitives'); @@ -16,7 +16,7 @@ $(document).ready(function() { }); test("objects: values", function() { - equals(_.values({one : 1, two : 2}).join(', '), '1, 2', 'can extract the values from an object'); + equal(_.values({one : 1, two : 2}).join(', '), '1, 2', 'can extract the values from an object'); }); test("objects: functions", function() { @@ -25,20 +25,20 @@ $(document).ready(function() { var Animal = function(){}; Animal.prototype.run = function(){}; - equals(_.functions(new Animal).join(''), 'run', 'also looks up functions on the prototype'); + equal(_.functions(new Animal).join(''), 'run', 'also looks up functions on the prototype'); }); test("objects: extend", function() { var result; - equals(_.extend({}, {a:'b'}).a, 'b', 'can extend an object with the attributes of another'); - equals(_.extend({a:'x'}, {a:'b'}).a, 'b', 'properties in source override destination'); - equals(_.extend({x:'x'}, {a:'b'}).x, 'x', 'properties not in source dont get overriden'); + equal(_.extend({}, {a:'b'}).a, 'b', 'can extend an object with the attributes of another'); + equal(_.extend({a:'x'}, {a:'b'}).a, 'b', 'properties in source override destination'); + equal(_.extend({x:'x'}, {a:'b'}).x, 'x', 'properties not in source dont get overriden'); result = _.extend({x:'x'}, {a:'a'}, {b:'b'}); ok(_.isEqual(result, {x:'x', a:'a', b:'b'}), 'can extend from multiple source objects'); result = _.extend({x:'x'}, {a:'a', x:2}, {a:'b'}); ok(_.isEqual(result, {x:2, a:'b'}), 'extending from multiple source objects last property trumps'); result = _.extend({}, {a: void 0, b: null}); - equals(_.keys(result).join(''), 'ab', 'extend does not copy undefined values'); + equal(_.keys(result).join(''), 'ab', 'extend does not copy undefined values'); }); test("objects: defaults", function() { @@ -46,30 +46,30 @@ $(document).ready(function() { var options = {zero: 0, one: 1, empty: "", nan: NaN, string: "string"}; _.defaults(options, {zero: 1, one: 10, twenty: 20}); - equals(options.zero, 0, 'value exists'); - equals(options.one, 1, 'value exists'); - equals(options.twenty, 20, 'default applied'); + equal(options.zero, 0, 'value exists'); + equal(options.one, 1, 'value exists'); + equal(options.twenty, 20, 'default applied'); _.defaults(options, {empty: "full"}, {nan: "nan"}, {word: "word"}, {word: "dog"}); - equals(options.empty, "", 'value exists'); + equal(options.empty, "", 'value exists'); ok(_.isNaN(options.nan), "NaN isn't overridden"); - equals(options.word, "word", 'new value is added, first one wins'); + equal(options.word, "word", 'new value is added, first one wins'); }); test("objects: clone", function() { var moe = {name : 'moe', lucky : [13, 27, 34]}; var clone = _.clone(moe); - equals(clone.name, 'moe', 'the clone as the attributes of the original'); + equal(clone.name, 'moe', 'the clone as the attributes of the original'); clone.name = 'curly'; ok(clone.name == 'curly' && moe.name == 'moe', 'clones can change shallow attributes without affecting the original'); clone.lucky.push(101); - equals(_.last(moe.lucky), 101, 'changes to deep attributes are shared with the original'); + equal(_.last(moe.lucky), 101, 'changes to deep attributes are shared with the original'); - equals(_.clone(undefined), void 0, 'non objects should not be changed by clone'); - equals(_.clone(1), 1, 'non objects should not be changed by clone'); - equals(_.clone(null), null, 'non objects should not be changed by clone'); + equal(_.clone(undefined), 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'); }); test("objects: isEqual", function() { @@ -291,7 +291,7 @@ $(document).ready(function() { // Chaining. ok(!_.isEqual(_({x: 1, y: undefined}).chain(), _({x: 1, z: 2}).chain()), 'Chained objects containing different values are not equal'); - equals(_({x: 1, y: 2}).chain().isEqual(_({x: 1, y: 2}).chain()).value(), true, '`isEqual` can be chained'); + equal(_({x: 1, y: 2}).chain().isEqual(_({x: 1, y: 2}).chain()).value(), true, '`isEqual` can be chained'); // Custom `isEqual` methods. var isEqualObj = {isEqual: function (o) { return o.isEqual == this.isEqual; }, unique: {}}; @@ -522,8 +522,8 @@ $(document).ready(function() { var intercepted = null; var interceptor = function(obj) { intercepted = obj; }; var returned = _.tap(1, interceptor); - equals(intercepted, 1, "passes tapped object to interceptor"); - equals(returned, 1, "returns tapped object"); + equal(intercepted, 1, "passes tapped object to interceptor"); + equal(returned, 1, "returns tapped object"); returned = _([1,2,3]).chain(). map(function(n){ return n * 2; }). diff --git a/test/utility.js b/test/utility.js index 7bc5cb44b..6207d3be3 100644 --- a/test/utility.js +++ b/test/utility.js @@ -6,19 +6,19 @@ $(document).ready(function() { var underscore = _.noConflict(); ok(underscore.isUndefined(_), "The '_' variable has been returned to its previous state."); var intersection = underscore.intersect([-1, 0, 1, 2], [1, 2, 3, 4]); - equals(intersection.join(', '), '1, 2', 'but the intersection function still works'); + equal(intersection.join(', '), '1, 2', 'but the intersection function still works'); window._ = underscore; }); test("utility: identity", function() { var moe = {name : 'moe'}; - equals(_.identity(moe), moe, 'moe is the same as his identity'); + equal(_.identity(moe), moe, 'moe is the same as his identity'); }); test("utility: uniqueId", function() { var ids = [], i = 0; while(i++ < 100) ids.push(_.uniqueId()); - equals(_.uniq(ids).length, ids.length, 'can generate a globally-unique stream of ids'); + equal(_.uniq(ids).length, ids.length, 'can generate a globally-unique stream of ids'); }); test("utility: times", function() { @@ -37,38 +37,38 @@ $(document).ready(function() { return string.split('').reverse().join(''); } }); - equals(_.myReverse('panacea'), 'aecanap', 'mixed in a function to _'); - equals(_('champ').myReverse(), 'pmahc', 'mixed in a function to the OOP wrapper'); + equal(_.myReverse('panacea'), 'aecanap', 'mixed in a function to _'); + equal(_('champ').myReverse(), 'pmahc', 'mixed in a function to the OOP wrapper'); }); test("utility: _.escape", function() { - equals(_.escape("Curly & Moe"), "Curly & Moe"); - equals(_.escape("Curly & Moe"), "Curly &amp; Moe"); + equal(_.escape("Curly & Moe"), "Curly & Moe"); + equal(_.escape("Curly & Moe"), "Curly &amp; Moe"); }); test("utility: template", function() { var basicTemplate = _.template("<%= thing %> is gettin' on my noives!"); var result = basicTemplate({thing : 'This'}); - equals(result, "This is gettin' on my noives!", 'can do basic attribute interpolation'); + equal(result, "This is gettin' on my noives!", 'can do basic attribute interpolation'); var sansSemicolonTemplate = _.template("A <% this %> B"); - equals(sansSemicolonTemplate(), "A B"); + equal(sansSemicolonTemplate(), "A B"); var backslashTemplate = _.template("<%= thing %> is \\ridanculous"); - equals(backslashTemplate({thing: 'This'}), "This is \\ridanculous"); + equal(backslashTemplate({thing: 'This'}), "This is \\ridanculous"); var escapeTemplate = _.template('<%= a ? "checked=\\"checked\\"" : "" %>'); - equals(escapeTemplate({a: true}), 'checked="checked"', 'can handle slash escapes in interpolations.'); + equal(escapeTemplate({a: true}), 'checked="checked"', 'can handle slash escapes in interpolations.'); var fancyTemplate = _.template("
      <% \ for (key in people) { \ %>
    • <%= people[key] %>
    • <% } %>
    "); result = fancyTemplate({people : {moe : "Moe", larry : "Larry", curly : "Curly"}}); - equals(result, "
    • Moe
    • Larry
    • Curly
    ", 'can run arbitrary javascript in templates'); + equal(result, "
    • Moe
    • Larry
    • Curly
    ", 'can run arbitrary javascript in templates'); var escapedCharsInJavascriptTemplate = _.template("
      <% _.each(numbers.split('\\n'), function(item) { %>
    • <%= item %>
    • <% }) %>
    "); result = escapedCharsInJavascriptTemplate({numbers: "one\ntwo\nthree\nfour"}); - equals(result, "
    • one
    • two
    • three
    • four
    ", 'Can use escaped characters (e.g. \\n) in Javascript'); + equal(result, "
    • one
    • two
    • three
    • four
    ", 'Can use escaped characters (e.g. \\n) in Javascript'); var namespaceCollisionTemplate = _.template("<%= pageCount %> <%= thumbnails[pageCount] %> <% _.each(thumbnails, function(p) { %>
    \">
    <% }); %>"); result = namespaceCollisionTemplate({ @@ -79,36 +79,36 @@ $(document).ready(function() { 3: "p3-thumbnail.gif" } }); - equals(result, "3 p3-thumbnail.gif
    "); + equal(result, "3 p3-thumbnail.gif
    "); var noInterpolateTemplate = _.template("

    Just some text. Hey, I know this is silly but it aids consistency.

    "); result = noInterpolateTemplate(); - equals(result, "

    Just some text. Hey, I know this is silly but it aids consistency.

    "); + equal(result, "

    Just some text. Hey, I know this is silly but it aids consistency.

    "); var quoteTemplate = _.template("It's its, not it's"); - equals(quoteTemplate({}), "It's its, not it's"); + equal(quoteTemplate({}), "It's its, not it's"); var quoteInStatementAndBody = _.template("<%\ if(foo == 'bar'){ \ %>Statement quotes and 'quotes'.<% } %>"); - equals(quoteInStatementAndBody({foo: "bar"}), "Statement quotes and 'quotes'."); + equal(quoteInStatementAndBody({foo: "bar"}), "Statement quotes and 'quotes'."); var withNewlinesAndTabs = _.template('This\n\t\tis: <%= x %>.\n\tok.\nend.'); - equals(withNewlinesAndTabs({x: 'that'}), 'This\n\t\tis: that.\n\tok.\nend.'); + equal(withNewlinesAndTabs({x: 'that'}), 'This\n\t\tis: that.\n\tok.\nend.'); var template = _.template("<%- value %>"); var result = template({value: " diff --git a/test/utility.js b/test/utility.js index 6207d3be3..4a59faf1f 100644 --- a/test/utility.js +++ b/test/utility.js @@ -1,6 +1,14 @@ $(document).ready(function() { - module("Utility"); + var templateSettings = _.templateSettings; + + module("Utility", { + + teardown: function() { + _.templateSettings = templateSettings; + } + + }); test("utility: noConflict", function() { var underscore = _.noConflict(); diff --git a/underscore.js b/underscore.js index 88da6e1d8..c0b4a84e2 100644 --- a/underscore.js +++ b/underscore.js @@ -905,7 +905,15 @@ // Within an interpolation, evaluation, or escaping, remove HTML escaping // that had been previously added. var unescape = function(code) { - return code.replace(/\\\\/g, '\\').replace(/\\'/g, "'"); + return code.replace(/\\(\\|'|r|n|t)/g, function(match, char) { + switch (char) { + case '\\': return '\\'; + case "'": return "'"; + case 'r': return '\r'; + case 'n': return '\n'; + case 't': return '\t'; + } + }); }; // JavaScript micro-templating, similar to John Resig's implementation. @@ -917,18 +925,18 @@ 'with(obj||{}){__p.push(\'' + str.replace(/\\/g, '\\\\') .replace(/'/g, "\\'") + .replace(/\r/g, '\\r') + .replace(/\n/g, '\\n') + .replace(/\t/g, '\\t') .replace(c.escape || noMatch, function(match, code) { - return "',_.escape(" + unescape(code) + "),'"; + return "',_.escape(" + unescape(code) + "),\n'"; }) .replace(c.interpolate || noMatch, function(match, code) { - return "'," + unescape(code) + ",'"; + return "'," + unescape(code) + ",\n'"; }) .replace(c.evaluate || noMatch, function(match, code) { - return "');" + unescape(code).replace(/[\r\n\t]/g, ' ') + ";__p.push('"; + return "');" + unescape(code) + ";\n__p.push('"; }) - .replace(/\r/g, '\\r') - .replace(/\n/g, '\\n') - .replace(/\t/g, '\\t') + "');}return __p.join('');"; var func = new Function('obj', '_', tmpl); if (data) return func(data, _); From 826e743262792c8cca93c38d2c774a34c81c7cdd Mon Sep 17 00:00:00 2001 From: Brad Dunbar Date: Mon, 12 Mar 2012 19:15:18 -0400 Subject: [PATCH 24/66] Handle \u2028 & \u2029 in _.template. --- test/utility.js | 5 +++++ underscore.js | 6 +++++- 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/test/utility.js b/test/utility.js index 4a59faf1f..c285751ea 100644 --- a/test/utility.js +++ b/test/utility.js @@ -160,4 +160,9 @@ $(document).ready(function() { equal(templateWithNull({planet : "world"}), "a null undefined world", "can handle missing escape and evaluate settings"); }); + test('_.template handles \\u2028 & \\u2029', function() { + var tmpl = _.template('

    \u2028<%= "\\u2028\\u2029" %>\u2029

    '); + strictEqual(tmpl(), '

    \u2028\u2028\u2029\u2029

    '); + }); + }); diff --git a/underscore.js b/underscore.js index c0b4a84e2..b956e21ce 100644 --- a/underscore.js +++ b/underscore.js @@ -905,13 +905,15 @@ // Within an interpolation, evaluation, or escaping, remove HTML escaping // that had been previously added. var unescape = function(code) { - return code.replace(/\\(\\|'|r|n|t)/g, function(match, char) { + return code.replace(/\\(\\|'|r|n|t|u2028|u2029)/g, function(match, char) { switch (char) { case '\\': return '\\'; case "'": return "'"; case 'r': return '\r'; case 'n': return '\n'; case 't': return '\t'; + case 'u2028': return '\u2028'; + case 'u2029': return '\u2029'; } }); }; @@ -928,6 +930,8 @@ .replace(/\r/g, '\\r') .replace(/\n/g, '\\n') .replace(/\t/g, '\\t') + .replace(/\u2028/g, '\\u2028') + .replace(/\u2029/g, '\\u2029') .replace(c.escape || noMatch, function(match, code) { return "',_.escape(" + unescape(code) + "),\n'"; }) From f5eb4b09157384c69f28e5cda38c620af8200c41 Mon Sep 17 00:00:00 2001 From: Brad Dunbar Date: Wed, 14 Mar 2012 08:25:01 -0400 Subject: [PATCH 25/66] Clean up _.template. * Cache regexes. * Use object properties for lookup instead of switch. --- underscore.js | 59 ++++++++++++++++++++++++++++----------------------- 1 file changed, 32 insertions(+), 27 deletions(-) diff --git a/underscore.js b/underscore.js index b956e21ce..4470eab44 100644 --- a/underscore.js +++ b/underscore.js @@ -902,19 +902,27 @@ // guaranteed not to match. var noMatch = /.^/; + // Certain characters need to be escaped so that they can be put into a + // string literal. + var escapes = { + '\\': '\\', + "'": "'", + 'r': '\r', + 'n': '\n', + 't': '\t', + 'u2028': '\u2028', + 'u2029': '\u2029' + }; + + for (var p in escapes) escapes[escapes[p]] = p; + var escaper = /\\|'|\r|\n|\t|\u2028|\u2029/g; + var unescaper = /\\(\\|'|r|n|t|u2028|u2029)/g; + // Within an interpolation, evaluation, or escaping, remove HTML escaping // that had been previously added. var unescape = function(code) { - return code.replace(/\\(\\|'|r|n|t|u2028|u2029)/g, function(match, char) { - switch (char) { - case '\\': return '\\'; - case "'": return "'"; - case 'r': return '\r'; - case 'n': return '\n'; - case 't': return '\t'; - case 'u2028': return '\u2028'; - case 'u2029': return '\u2029'; - } + return code.replace(unescaper, function(match, escape) { + return escapes[escape]; }); }; @@ -925,23 +933,20 @@ var c = _.templateSettings; var tmpl = 'var __p=[],print=function(){__p.push.apply(__p,arguments);};' + 'with(obj||{}){__p.push(\'' + - str.replace(/\\/g, '\\\\') - .replace(/'/g, "\\'") - .replace(/\r/g, '\\r') - .replace(/\n/g, '\\n') - .replace(/\t/g, '\\t') - .replace(/\u2028/g, '\\u2028') - .replace(/\u2029/g, '\\u2029') - .replace(c.escape || noMatch, function(match, code) { - return "',_.escape(" + unescape(code) + "),\n'"; - }) - .replace(c.interpolate || noMatch, function(match, code) { - return "'," + unescape(code) + ",\n'"; - }) - .replace(c.evaluate || noMatch, function(match, code) { - return "');" + unescape(code) + ";\n__p.push('"; - }) - + "');}return __p.join('');"; + str + .replace(escaper, function(match) { + return '\\' + escapes[match]; + }) + .replace(c.escape || noMatch, function(match, code) { + return "',_.escape(" + unescape(code) + "),\n'"; + }) + .replace(c.interpolate || noMatch, function(match, code) { + return "'," + unescape(code) + ",\n'"; + }) + .replace(c.evaluate || noMatch, function(match, code) { + return "');" + unescape(code) + ";\n__p.push('"; + }) + + "');}return __p.join('');"; var func = new Function('obj', '_', tmpl); if (data) return func(data, _); return function(data) { From 666049ac5db5634ba8694e64684b31ab2d1f1549 Mon Sep 17 00:00:00 2001 From: Brad Dunbar Date: Sat, 17 Mar 2012 13:02:17 -0400 Subject: [PATCH 26/66] Add utility function `getValue`. --- index.html | 195 ++++++++++++++++++++++++++---------------------- test/utility.js | 9 +++ underscore.js | 6 ++ 3 files changed, 119 insertions(+), 91 deletions(-) diff --git a/index.html b/index.html index 9165f1428..c6818b7a7 100644 --- a/index.html +++ b/index.html @@ -3,7 +3,7 @@ - + Underscore.js - +
    @@ -297,7 +298,7 @@ A complete Test & Benchmark Suite is included for your perusal.

    - +

    You may also read through the annotated source code.

    @@ -327,7 +328,7 @@

    Downloads (Right-click, and u < 4kb, Minified and Gzipped - +
    Upgrade warning: version 1.3.0 removes AMD (RequireJS) support.
    @@ -525,7 +526,7 @@

    Collection Functions (Arrays or Objects)

    sortBy_.sortBy(list, iterator, [context])
    - Returns a sorted copy of list, ranked in ascending order by the + Returns a sorted copy of list, ranked in ascending order by the results of running each value through iterator.

    @@ -565,7 +566,7 @@ 

    Collection Functions (Arrays or Objects)

    shuffle_.shuffle(list)
    - Returns a shuffled copy of the list, using a version of the + Returns a shuffled copy of the list, using a version of the Fisher-Yates shuffle.

    @@ -751,7 +752,7 @@ 

    Array Functions


    Returns the index at which value can be found in the array, or -1 if value is not present in the array. Uses the native - indexOf function unless it's missing. If you're working with a + indexOf function unless it's missing. If you're working with a large array, and you know that the array is already sorted, pass true for isSorted to use a faster binary search.

    @@ -836,7 +837,7 @@

    Function (uh, ahem) Functions

    memoize_.memoize(function, [hashFunction])
    Memoizes a given function by caching the computed result. Useful - for speeding up slow-running computations. If passed an optional + for speeding up slow-running computations. If passed an optional hashFunction, it will be used to compute the hash key for storing the result, based on the arguments to the original function. The default hashFunction just uses the first argument to the memoized function @@ -877,8 +878,8 @@

    Function (uh, ahem) Functions

    throttle_.throttle(function, wait)
    - Creates and returns a new, throttled version of the passed function, - that, when invoked repeatedly, will only actually call the original function + Creates and returns a new, throttled version of the passed function, + that, when invoked repeatedly, will only actually call the original function at most once per every wait milliseconds. Useful for rate-limiting events that occur faster than you can keep up with. @@ -892,11 +893,11 @@

    Function (uh, ahem) Functions

    debounce_.debounce(function, wait)
    Creates and returns a new debounced version of the passed function that - will postpone its execution until after - wait milliseconds have elapsed since the last time it - was invoked. Useful for implementing behavior that should only happen - after the input has stopped arriving. For example: rendering a - preview of a Markdown comment, recalculating a layout after the window + will postpone its execution until after + wait milliseconds have elapsed since the last time it + was invoked. Useful for implementing behavior that should only happen + after the input has stopped arriving. For example: rendering a + preview of a Markdown comment, recalculating a layout after the window has stopped being resized, and so on.

    @@ -907,7 +908,7 @@ 

    Function (uh, ahem) Functions

    once_.once(function)
    - Creates a version of the function that can only be called one time. + Creates a version of the function that can only be called one time. Repeated calls to the modified function will have no effect, returning the value from the original call. Useful for initialization functions, instead of having to set a boolean flag and then check it later. @@ -922,7 +923,7 @@

    Function (uh, ahem) Functions

    after_.after(count, function)
    - Creates a version of the function that will only be run after first + Creates a version of the function that will only be run after first being called count times. Useful for grouping asynchronous responses, where you want to be sure that all the async calls have finished, before proceeding. @@ -930,7 +931,7 @@

    Function (uh, ahem) Functions

     var renderNotes = _.after(notes.length, render);
     _.each(notes, function(note) {
    -  note.asyncSave({success: renderNotes}); 
    +  note.asyncSave({success: renderNotes});
     });
     // renderNotes is run once, after all notes have saved.
     
    @@ -1058,9 +1059,9 @@

    Object Functions

    has_.has(object, key)
    - Does the object contain the given key? Identical to + Does the object contain the given key? Identical to object.hasOwnProperty(key), but uses a safe reference to the - hasOwnProperty function, in case it's been + hasOwnProperty function, in case it's been overridden accidentally.

    @@ -1238,7 +1239,7 @@ 

    Object Functions

    _.isUndefined(window.missingVariable); => true
    - +

    Utility Functions

    @@ -1275,7 +1276,7 @@

    Utility Functions

    mixin_.mixin(object)
    Allows you to extend Underscore with your own utility functions. Pass - a hash of {name: function} definitions to have your functions + a hash of {name: function} definitions to have your functions added to the Underscore object, as well as the OOP wrapper.

    @@ -1302,13 +1303,25 @@ 

    Utility Functions

    escape_.escape(string)
    - Escapes a string for insertion into HTML, replacing + Escapes a string for insertion into HTML, replacing &, <, >, ", ', and / characters.

     _.escape('Curly, Larry & Moe');
     => "Curly, Larry &amp; Moe"
    +

    + getValue_.getValue(object, property) +
    + Returns a value from an object as a property or as a function. +

    +
    +var object = {cheese: 'crumpets', stuff: function(){ return 'nonsense'; }};
    +_.getValue(object, 'cheese');
    +=> "crumpets"
    +_.getValue(object, 'stuff');
    +=> "nonsense"
    +

    template_.template(templateString, [context])
    @@ -1340,7 +1353,7 @@

    Utility Functions

    You can also use print from within JavaScript code. This is sometimes more convenient than using <%= ... %>.

    - +
     var compiled = _.template("<% print('Hello ' + epithet); %>");
     compiled({epithet: "stooge"});
    @@ -1349,8 +1362,8 @@ 

    Utility Functions

    If ERB-style delimiters aren't your cup of tea, you can change Underscore's template settings to use different symbols to set off interpolated code. - Define an interpolate regex to match expressions that should be - interpolated verbatim, an escape regex to match expressions that should + Define an interpolate regex to match expressions that should be + interpolated verbatim, an escape regex to match expressions that should be inserted after being HTML escaped, and an evaluate regex to match expressions that should be evaluated without insertion into the resulting string. You may define or omit any combination of the three. @@ -1451,7 +1464,7 @@

    The source is available on GitHub.

    - +

    Underscore.php, a PHP port of the functions that are applicable in both languages. @@ -1459,17 +1472,17 @@

    The source is available on GitHub.

    - +

    Underscore-perl, - a Perl port of many of the Underscore.js functions, - aimed at on Perl hashes and arrays, also + a Perl port of many of the Underscore.js functions, + aimed at on Perl hashes and arrays, also available on GitHub.

    - +

    Underscore.string, - an Underscore extension that adds functions for string-manipulation: + an Underscore extension that adds functions for string-manipulation: trim, startsWith, contains, capitalize, reverse, sprintf, and more.

    @@ -1488,7 +1501,7 @@ Functional JavaScript, which includes comprehensive higher-order function support as well as string lambdas.

    - +

    Michael Aufreiter's Data.js, a data manipulation + persistence library for JavaScript. @@ -1499,7 +1512,7 @@

    Change Log

    - +

    1.3.1Jan. 23, 2012

      @@ -1510,7 +1523,7 @@

      Change Log

      Added _.collect as an alias for _.map. Smalltalkers, rejoice.
    • - Reverted an old change so that _.extend will correctly copy + Reverted an old change so that _.extend will correctly copy over keys with undefined values again.
    • @@ -1518,7 +1531,7 @@

      Change Log

    - +

    1.3.0Jan. 11, 2012

      @@ -1529,12 +1542,12 @@

      Change Log

    - +

    1.2.4Jan. 4, 2012

    • - You now can (and probably should) write _.chain(list) + You now can (and probably should) write _.chain(list) instead of _(list).chain().
    • @@ -1551,7 +1564,7 @@

      Change Log

    - +

    1.2.3Dec. 7, 2011

      @@ -1564,12 +1577,12 @@

      Change Log

    • Both _.reduce and _.reduceRight can now be passed an - explicitly undefined value. (There's no reason why you'd + explicitly undefined value. (There's no reason why you'd want to do this.)

    - +

    1.2.2Nov. 14, 2011

      @@ -1588,22 +1601,22 @@

      Change Log

    • _.after(callback, 0) will now trigger the callback immediately, - making "after" easier to use with asynchronous APIs (#366). + making "after" easier to use with asynchronous APIs (#366).

    - +

    1.2.1Oct. 24, 2011

    • - Several important bug fixes for _.isEqual, which should now - do better on mutated Arrays, and on non-Array objects with + Several important bug fixes for _.isEqual, which should now + do better on mutated Arrays, and on non-Array objects with length properties. (#329)
    • jrburke contributed Underscore exporting for AMD module loaders, - and tonylukasavage for Appcelerator Titanium. + and tonylukasavage for Appcelerator Titanium. (#335, #338)
    • @@ -1611,7 +1624,7 @@

      Change Log

      grouping values by a particular common property.
    • - _.throttle'd functions now fire immediately upon invocation, + _.throttle'd functions now fire immediately upon invocation, and are rate-limited thereafter (#170, #266).
    • @@ -1619,7 +1632,7 @@

      Change Log

    • The _.bind function now also works on constructors, a-la - ES5 ... but you would never want to use _.bind on a + ES5 ... but you would never want to use _.bind on a constructor function.
    • @@ -1631,25 +1644,25 @@

      Change Log

    - +

    1.2.0Oct. 5, 2011

    • - The _.isEqual function now + The _.isEqual function now supports true deep equality comparisons, with checks for cyclic structures, thanks to Kit Cambridge.
    • - Underscore templates now support HTML escaping interpolations, using - <%- ... %> syntax. + Underscore templates now support HTML escaping interpolations, using + <%- ... %> syntax.
    • - Ryan Tenney contributed _.shuffle, which uses a modified - Fisher-Yates to give you a shuffled copy of an array. + Ryan Tenney contributed _.shuffle, which uses a modified + Fisher-Yates to give you a shuffled copy of an array.
    • - _.uniq can now be passed an optional iterator, to determine by + _.uniq can now be passed an optional iterator, to determine by what criteria an object should be considered unique.
    • @@ -1658,22 +1671,22 @@

      Change Log

    • A new _.initial function was added, as a mirror of _.rest, - which returns all the initial values of a list (except the last N). + which returns all the initial values of a list (except the last N).

    - +

    1.1.7July 13, 2011
    Added _.groupBy, which aggregates a collection into groups of like items. - Added _.union and _.difference, to complement the + Added _.union and _.difference, to complement the (re-named) _.intersection. Various improvements for support of sparse arrays. _.toArray now returns a clone, if directly passed an array. _.functions now also returns the names of functions that are present in the prototype chain.

    - +

    1.1.6April 18, 2011
    Added _.after, which will return a function that only runs after @@ -1684,30 +1697,30 @@

    Change Log

    _.extend no longer copies keys when the value is undefined. _.bind now errors when trying to bind an undefined value.

    - +

    1.1.5Mar 20, 2011
    Added an _.defaults function, for use merging together JS objects representing default options. Added an _.once function, for manufacturing functions that should only ever execute a single time. - _.bind now delegates to the native ECMAScript 5 version, + _.bind now delegates to the native ECMAScript 5 version, where available. _.keys now throws an error when used on non-Object values, as in ECMAScript 5. Fixed a bug with _.keys when used over sparse arrays.

    - +

    1.1.4Jan 9, 2011
    - Improved compliance with ES5's Array methods when passing null + Improved compliance with ES5's Array methods when passing null as a value. _.wrap now correctly sets this for the wrapped function. _.indexOf now takes an optional flag for finding the insertion index in an array that is guaranteed to already be sorted. Avoiding the use of .callee, to allow _.isArray to work properly in ES5's strict mode.

    - +

    1.1.3Dec 1, 2010
    In CommonJS, Underscore may now be required with just:
    @@ -1719,26 +1732,26 @@

    Change Log

    Improved the isType family of functions for better interoperability with Internet Explorer host objects. _.template now correctly escapes backslashes in templates. - Improved _.reduce compatibility with the ECMA5 version: + Improved _.reduce compatibility with the ECMA5 version: if you don't pass an initial value, the first item in the collection is used. _.each no longer returns the iterated collection, for improved consistency with ES5's forEach.

    - +

    1.1.2
    - Fixed _.contains, which was mistakenly pointing at - _.intersect instead of _.include, like it should + Fixed _.contains, which was mistakenly pointing at + _.intersect instead of _.include, like it should have been. Added _.unique as an alias for _.uniq.

    - +

    1.1.1
    Improved the speed of _.template, and its handling of multiline - interpolations. Ryan Tenney contributed optimizations to many Underscore + interpolations. Ryan Tenney contributed optimizations to many Underscore functions. An annotated version of the source code is now available.

    - +

    1.1.0
    The method signature of _.reduce has been changed to match @@ -1747,33 +1760,33 @@

    Change Log

    called with no arguments, and preserves whitespace. _.contains is a new alias for _.include.

    - +

    1.0.4
    - Andri Möll contributed the _.memoize - function, which can be used to speed up expensive repeated computations + Andri Möll contributed the _.memoize + function, which can be used to speed up expensive repeated computations by caching the results.

    - +

    1.0.3
    Patch that makes _.isEqual return false if any property of the compared object has a NaN value. Technically the correct thing to do, but of questionable semantics. Watch out for NaN comparisons.

    - +

    1.0.2
    Fixes _.isArguments in recent versions of Opera, which have arguments objects as real Arrays.

    - +

    1.0.1
    - Bugfix for _.isEqual, when comparing two objects with the same + Bugfix for _.isEqual, when comparing two objects with the same number of undefined keys, but with different names.

    - +

    1.0.0
    Things have been stable for many months now, so Underscore is now @@ -1781,15 +1794,15 @@

    Change Log

    include _.isBoolean, and the ability to have _.extend take multiple source objects.

    - +

    0.6.0
    - Major release. Incorporates a number of + Major release. Incorporates a number of Mile Frawley's refactors for safer duck-typing on collection functions, and cleaner internals. A new _.mixin method that allows you to extend Underscore with utility - functions of your own. Added _.times, which works the same as in - Ruby or Prototype.js. Native support for ECMAScript 5's Array.isArray, + functions of your own. Added _.times, which works the same as in + Ruby or Prototype.js. Native support for ECMAScript 5's Array.isArray, and Object.keys.

    @@ -1847,7 +1860,7 @@

    Change Log

    0.5.1
    Added an _.isArguments function. Lots of little safety checks and optimizations contributed by - Noah Sloan and + Noah Sloan and Andri Möll.

    diff --git a/test/utility.js b/test/utility.js index c285751ea..5f2485478 100644 --- a/test/utility.js +++ b/test/utility.js @@ -165,4 +165,13 @@ $(document).ready(function() { strictEqual(tmpl(), '

    \u2028\u2028\u2029\u2029

    '); }); + test('getValue calls functions and returns primitives', function() { + var obj = {w: '', x: 'x', y: function(){ return 'y'; }}; + strictEqual(_.getValue(obj, 'w'), ''); + strictEqual(_.getValue(obj, 'x'), 'x'); + strictEqual(_.getValue(obj, 'y'), 'y'); + strictEqual(_.getValue(obj, 'z'), undefined); + strictEqual(_.getValue(null, 'x'), null); + }); + }); diff --git a/underscore.js b/underscore.js index b956e21ce..720801588 100644 --- a/underscore.js +++ b/underscore.js @@ -873,6 +873,12 @@ return (''+string).replace(/&/g, '&').replace(//g, '>').replace(/"/g, '"').replace(/'/g, ''').replace(/\//g,'/'); }; + // Get a value from an object as a property or as a function. + _.getValue = function(object, prop) { + if (object == null) return null; + return _.isFunction(object[prop]) ? object[prop]() : object[prop]; + }; + // Add your own custom functions to the Underscore object, ensuring that // they're correctly added to the OOP wrapper as well. _.mixin = function(obj) { From d0e7b397c1427bf69f2c03d45c5ce31dc60f8612 Mon Sep 17 00:00:00 2001 From: Brad Dunbar Date: Mon, 19 Mar 2012 14:24:59 -0400 Subject: [PATCH 27/66] Cache property value. --- underscore.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/underscore.js b/underscore.js index 720801588..24dcc72be 100644 --- a/underscore.js +++ b/underscore.js @@ -876,7 +876,8 @@ // Get a value from an object as a property or as a function. _.getValue = function(object, prop) { if (object == null) return null; - return _.isFunction(object[prop]) ? object[prop]() : object[prop]; + var value = object[prop]; + return _.isFunction(value) ? value() : value; }; // Add your own custom functions to the Underscore object, ensuring that From 5c7ccb21ef5404dbbf4d925d03f35e49967e9a3a Mon Sep 17 00:00:00 2001 From: Brad Dunbar Date: Mon, 19 Mar 2012 14:33:38 -0400 Subject: [PATCH 28/66] Rename `getValue` to `result`. --- index.html | 10 +++++----- test/utility.js | 12 ++++++------ underscore.js | 2 +- 3 files changed, 12 insertions(+), 12 deletions(-) diff --git a/index.html b/index.html index c6818b7a7..f2a00cae3 100644 --- a/index.html +++ b/index.html @@ -245,7 +245,7 @@
  • - mixin
  • - uniqueId
  • - escape
  • -
  • - getValue
  • +
  • - result
  • - template
  • @@ -1310,16 +1310,16 @@

    Utility Functions

    _.escape('Curly, Larry & Moe'); => "Curly, Larry &amp; Moe"
    -

    - getValue_.getValue(object, property) +

    + result_.result(object, property)
    Returns a value from an object as a property or as a function.

     var object = {cheese: 'crumpets', stuff: function(){ return 'nonsense'; }};
    -_.getValue(object, 'cheese');
    +_.result(object, 'cheese');
     => "crumpets"
    -_.getValue(object, 'stuff');
    +_.result(object, 'stuff');
     => "nonsense"

    diff --git a/test/utility.js b/test/utility.js index 5f2485478..ec9960eae 100644 --- a/test/utility.js +++ b/test/utility.js @@ -165,13 +165,13 @@ $(document).ready(function() { strictEqual(tmpl(), '

    \u2028\u2028\u2029\u2029

    '); }); - test('getValue calls functions and returns primitives', function() { + test('result calls functions and returns primitives', function() { var obj = {w: '', x: 'x', y: function(){ return 'y'; }}; - strictEqual(_.getValue(obj, 'w'), ''); - strictEqual(_.getValue(obj, 'x'), 'x'); - strictEqual(_.getValue(obj, 'y'), 'y'); - strictEqual(_.getValue(obj, 'z'), undefined); - strictEqual(_.getValue(null, 'x'), null); + strictEqual(_.result(obj, 'w'), ''); + strictEqual(_.result(obj, 'x'), 'x'); + strictEqual(_.result(obj, 'y'), 'y'); + strictEqual(_.result(obj, 'z'), undefined); + strictEqual(_.result(null, 'x'), null); }); }); diff --git a/underscore.js b/underscore.js index 24dcc72be..c00438cc5 100644 --- a/underscore.js +++ b/underscore.js @@ -874,7 +874,7 @@ }; // Get a value from an object as a property or as a function. - _.getValue = function(object, prop) { + _.result = function(object, prop) { if (object == null) return null; var value = object[prop]; return _.isFunction(value) ? value() : value; From 5545a3b68da905b7cc956c1070f45cf9127ec045 Mon Sep 17 00:00:00 2001 From: Brad Dunbar Date: Mon, 19 Mar 2012 14:43:20 -0400 Subject: [PATCH 29/66] Dispense with abbreviation. --- underscore.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/underscore.js b/underscore.js index c00438cc5..eff693d3a 100644 --- a/underscore.js +++ b/underscore.js @@ -874,9 +874,9 @@ }; // Get a value from an object as a property or as a function. - _.result = function(object, prop) { + _.result = function(object, property) { if (object == null) return null; - var value = object[prop]; + var value = object[property]; return _.isFunction(value) ? value() : value; }; From 9a27b1b083f8651e95f3622bfe5a338f36f7511a Mon Sep 17 00:00:00 2001 From: Brad Dunbar Date: Mon, 19 Mar 2012 15:07:33 -0400 Subject: [PATCH 30/66] Clarify documentation for _.result. --- index.html | 3 ++- underscore.js | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/index.html b/index.html index f2a00cae3..7ebdc3f6e 100644 --- a/index.html +++ b/index.html @@ -1313,7 +1313,8 @@

    Utility Functions

    result_.result(object, property)
    - Returns a value from an object as a property or as a function. + If the value of the named property is a function then invoke it. + Otherwise, return its value.

     var object = {cheese: 'crumpets', stuff: function(){ return 'nonsense'; }};
    diff --git a/underscore.js b/underscore.js
    index 6c9512594..429a9b142 100644
    --- a/underscore.js
    +++ b/underscore.js
    @@ -873,7 +873,8 @@
         return (''+string).replace(/&/g, '&').replace(//g, '>').replace(/"/g, '"').replace(/'/g, ''').replace(/\//g,'/');
       };
     
    -  // Get a value from an object as a property or as a function.
    +  // If the value of the named property is a function then invoke it.
    +  // Otherwise, return its value.
       _.result = function(object, property) {
         if (object == null) return null;
         var value = object[property];
    
    From 33be5c62b8fe39b185024ea7d4e8b0e196f29d6b Mon Sep 17 00:00:00 2001
    From: Brad Dunbar 
    Date: Mon, 19 Mar 2012 15:45:44 -0400
    Subject: [PATCH 31/66] Make a small documentation change.
    
    ---
     index.html    | 3 +--
     underscore.js | 4 ++--
     2 files changed, 3 insertions(+), 4 deletions(-)
    
    diff --git a/index.html b/index.html
    index 7ebdc3f6e..9b163c16b 100644
    --- a/index.html
    +++ b/index.html
    @@ -1313,8 +1313,7 @@ 

    Utility Functions

    result_.result(object, property)
    - If the value of the named property is a function then invoke it. - Otherwise, return its value. + If the value of the named property is a function then invoke it; otherwise, return it.

     var object = {cheese: 'crumpets', stuff: function(){ return 'nonsense'; }};
    diff --git a/underscore.js b/underscore.js
    index 429a9b142..21a45c0d9 100644
    --- a/underscore.js
    +++ b/underscore.js
    @@ -873,8 +873,8 @@
         return (''+string).replace(/&/g, '&').replace(//g, '>').replace(/"/g, '"').replace(/'/g, ''').replace(/\//g,'/');
       };
     
    -  // If the value of the named property is a function then invoke it.
    -  // Otherwise, return its value.
    +  // If the value of the named property is a function then invoke it;
    +  // otherwise, return it.
       _.result = function(object, property) {
         if (object == null) return null;
         var value = object[property];
    
    From 2d3edb88f0bfda175082a2f55cf3cda980e5e489 Mon Sep 17 00:00:00 2001
    From: Raymond May Jr 
    Date: Mon, 19 Mar 2012 19:08:36 -0500
    Subject: [PATCH 32/66] _.size ludicrous speed improvement
    
    ---
     underscore.js | 6 +++++-
     1 file changed, 5 insertions(+), 1 deletion(-)
    
    diff --git a/underscore.js b/underscore.js
    index 21a45c0d9..ba2ee7600 100644
    --- a/underscore.js
    +++ b/underscore.js
    @@ -309,7 +309,11 @@
     
       // Return the number of elements in an object.
       _.size = function(obj) {
    -    return _.toArray(obj).length;
    +      if (_.isArray(obj)) {
    +	  return obj.length;
    +      } else {
    +	  return _.keys(obj).length;
    +      }
       };
     
       // Array Functions
    
    From 5827e4a40a5a30f7a00b2bd1ab01111510e8a2d4 Mon Sep 17 00:00:00 2001
    From: Raymond May Jr 
    Date: Mon, 19 Mar 2012 19:10:59 -0500
    Subject: [PATCH 33/66] _.size ludicrous speed improvement - formatting
    
    ---
     underscore.js | 10 +++++-----
     1 file changed, 5 insertions(+), 5 deletions(-)
    
    diff --git a/underscore.js b/underscore.js
    index ba2ee7600..6f3ac8f47 100644
    --- a/underscore.js
    +++ b/underscore.js
    @@ -309,11 +309,11 @@
     
       // Return the number of elements in an object.
       _.size = function(obj) {
    -      if (_.isArray(obj)) {
    -	  return obj.length;
    -      } else {
    -	  return _.keys(obj).length;
    -      }
    +    if (_.isArray(obj)) {
    +      return obj.length;
    +    }else{
    +      return _.keys(obj).length;
    +    }
       };
     
       // Array Functions
    
    From 4c2a85f9c5f38ce466e9b827c2f7e3024196b579 Mon Sep 17 00:00:00 2001
    From: Raymond May Jr 
    Date: Tue, 20 Mar 2012 09:31:50 -0500
    Subject: [PATCH 34/66] size enhancement as ternary and _.size(arr) test case
    
    ---
     test/collections.js | 1 +
     underscore.js       | 6 +-----
     2 files changed, 2 insertions(+), 5 deletions(-)
    
    diff --git a/test/collections.js b/test/collections.js
    index 71ef5a71c..18b242769 100644
    --- a/test/collections.js
    +++ b/test/collections.js
    @@ -275,6 +275,7 @@ $(document).ready(function() {
     
       test('collections: size', function() {
         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');
       });
     
     });
    diff --git a/underscore.js b/underscore.js
    index 6f3ac8f47..c699c991b 100644
    --- a/underscore.js
    +++ b/underscore.js
    @@ -309,11 +309,7 @@
     
       // Return the number of elements in an object.
       _.size = function(obj) {
    -    if (_.isArray(obj)) {
    -      return obj.length;
    -    }else{
    -      return _.keys(obj).length;
    -    }
    +    return _.isArray(obj) ? obj.length : _.keys(obj).length;
       };
     
       // Array Functions
    
    From ebb9db4e8e62a8760d8d24cbda6b7c66dc211d0e Mon Sep 17 00:00:00 2001
    From: Brad Dunbar 
    Date: Wed, 21 Mar 2012 06:33:37 -0400
    Subject: [PATCH 35/66] _.result calls `property` with the correct context.
    
    ---
     test/utility.js | 4 ++--
     underscore.js   | 2 +-
     2 files changed, 3 insertions(+), 3 deletions(-)
    
    diff --git a/test/utility.js b/test/utility.js
    index ec9960eae..da374b59d 100644
    --- a/test/utility.js
    +++ b/test/utility.js
    @@ -166,10 +166,10 @@ $(document).ready(function() {
       });
     
       test('result calls functions and returns primitives', function() {
    -    var obj = {w: '', x: 'x', y: function(){ return 'y'; }};
    +    var obj = {w: '', x: 'x', y: function(){ return this.x; }};
         strictEqual(_.result(obj, 'w'), '');
         strictEqual(_.result(obj, 'x'), 'x');
    -    strictEqual(_.result(obj, 'y'), 'y');
    +    strictEqual(_.result(obj, 'y'), 'x');
         strictEqual(_.result(obj, 'z'), undefined);
         strictEqual(_.result(null, 'x'), null);
       });
    diff --git a/underscore.js b/underscore.js
    index c699c991b..aeaa8b1b0 100644
    --- a/underscore.js
    +++ b/underscore.js
    @@ -878,7 +878,7 @@
       _.result = function(object, property) {
         if (object == null) return null;
         var value = object[property];
    -    return _.isFunction(value) ? value() : value;
    +    return _.isFunction(value) ? value.call(object) : value;
       };
     
       // Add your own custom functions to the Underscore object, ensuring that
    
    From 85672298d9f90f7aea038aa3d439227412e3ac53 Mon Sep 17 00:00:00 2001
    From: Mark Rushakoff 
    Date: Thu, 22 Mar 2012 19:21:23 -0700
    Subject: [PATCH 36/66] Prefer equal(x, y) over ok(x == y)
    
    ---
     test/functions.js | 10 +++++-----
     1 file changed, 5 insertions(+), 5 deletions(-)
    
    diff --git a/test/functions.js b/test/functions.js
    index ff95f4368..086233960 100644
    --- a/test/functions.js
    +++ b/test/functions.js
    @@ -101,8 +101,8 @@ $(document).ready(function() {
         setTimeout(throttledIncr, 190);
         setTimeout(throttledIncr, 220);
         setTimeout(throttledIncr, 240);
    -    _.delay(function(){ ok(counter == 1, "incr was called immediately"); }, 30);
    -    _.delay(function(){ ok(counter == 4, "incr was throttled"); start(); }, 400);
    +    _.delay(function(){ equal(counter, 1, "incr was called immediately"); }, 30);
    +    _.delay(function(){ equal(counter, 4, "incr was throttled"); start(); }, 400);
       });
     
       asyncTest("functions: throttle arguments", 2, function() {
    @@ -122,7 +122,7 @@ $(document).ready(function() {
         var incr = function(){ counter++; };
         var throttledIncr = _.throttle(incr, 100);
         throttledIncr();
    -    _.delay(function(){ ok(counter == 1, "incr was called once"); start(); }, 220);
    +    _.delay(function(){ equal(counter, 1, "incr was called once"); start(); }, 220);
       });
     
       asyncTest("functions: throttle twice", 1, function() {
    @@ -130,7 +130,7 @@ $(document).ready(function() {
         var incr = function(){ counter++; };
         var throttledIncr = _.throttle(incr, 100);
         throttledIncr(); throttledIncr();
    -    _.delay(function(){ ok(counter == 2, "incr was called twice"); start(); }, 220);
    +    _.delay(function(){ equal(counter, 2, "incr was called twice"); start(); }, 220);
       });
     
       asyncTest("functions: debounce", 1, function() {
    @@ -143,7 +143,7 @@ $(document).ready(function() {
         setTimeout(debouncedIncr, 90);
         setTimeout(debouncedIncr, 120);
         setTimeout(debouncedIncr, 150);
    -    _.delay(function(){ ok(counter == 1, "incr was debounced"); start(); }, 220);
    +    _.delay(function(){ equal(counter, 1, "incr was debounced"); start(); }, 220);
       });
     
       asyncTest("functions: debounce asap", 2, function() {
    
    From 2055d745db5ee91e99476e837754d9bc052c3ab7 Mon Sep 17 00:00:00 2001
    From: Brad Dunbar 
    Date: Fri, 23 Mar 2012 12:52:50 -0400
    Subject: [PATCH 37/66] Attach template source to returned function.
    
    ---
     underscore.js | 28 +++++++++++++++-------------
     1 file changed, 15 insertions(+), 13 deletions(-)
    
    diff --git a/underscore.js b/underscore.js
    index aeaa8b1b0..1649df208 100644
    --- a/underscore.js
    +++ b/underscore.js
    @@ -938,28 +938,30 @@
       // Underscore templating handles arbitrary delimiters, preserves whitespace,
       // and correctly escapes quotes within interpolated code.
       _.template = function(str, data) {
    -    var c  = _.templateSettings;
    -    var tmpl = 'var __p=[],print=function(){__p.push.apply(__p,arguments);};' +
    +    var settings  = _.templateSettings;
    +    var source = 'var __p=[],print=function(){__p.push.apply(__p,arguments);};' +
           'with(obj||{}){__p.push(\'' +
           str
             .replace(escaper, function(match) {
               return '\\' + escapes[match];
             })
    -        .replace(c.escape || noMatch, function(match, code) {
    -          return "',_.escape(" + unescape(code) + "),\n'";
    +        .replace(settings.escape || noMatch, function(match, code) {
    +          return "',\n_.escape(" + unescape(code) + "),\n'";
             })
    -        .replace(c.interpolate || noMatch, function(match, code) {
    -          return "'," + unescape(code) + ",\n'";
    +        .replace(settings.interpolate || noMatch, function(match, code) {
    +          return "',\n" + unescape(code) + ",\n'";
             })
    -        .replace(c.evaluate || noMatch, function(match, code) {
    -          return "');" + unescape(code) + ";\n__p.push('";
    +        .replace(settings.evaluate || noMatch, function(match, code) {
    +          return "');\n" + unescape(code) + "\n;__p.push('";
             })
    -        + "');}return __p.join('');";
    -    var func = new Function('obj', '_', tmpl);
    -    if (data) return func(data, _);
    -    return function(data) {
    -      return func.call(this, data, _);
    +        + "');\n}\nreturn __p.join('');";
    +    var render = new Function('obj', '_', source);
    +    if (data) return render(data, _);
    +    var template = function(data) {
    +      return render.call(this, data, _);
         };
    +    template.source = 'function(obj, _){\n' + source + '\n}';
    +    return template;
       };
     
       // Add a "chain" function, which will delegate to the wrapper.
    
    From 705eff4826660d11ec2af4fa7aafd4bed92e66a1 Mon Sep 17 00:00:00 2001
    From: Raymond May Jr 
    Date: Sun, 25 Mar 2012 10:43:30 -0500
    Subject: [PATCH 38/66] add _.isFinite - verifies is a number between Inf &&
     -Inf
    
    ---
     underscore.js | 5 +++++
     1 file changed, 5 insertions(+)
    
    diff --git a/underscore.js b/underscore.js
    index aeaa8b1b0..98ffea186 100644
    --- a/underscore.js
    +++ b/underscore.js
    @@ -812,6 +812,11 @@
         return toString.call(obj) == '[object Number]';
       };
     
    +  // Is a givin number finite?
    +  _.isFinite = function(obj) {
    +    return obj > -1/0 && obj < 1/0 && obj === -obj;
    +  };
    +
       // Is the given value `NaN`?
       _.isNaN = function(obj) {
         // `NaN` is the only value for which `===` is not reflexive.
    
    From e27423586528b0947407b36dba6916cc33c61e9b Mon Sep 17 00:00:00 2001
    From: Raymond May Jr 
    Date: Sun, 25 Mar 2012 11:08:23 -0500
    Subject: [PATCH 39/66] _.isFinite fix and tests
    
    ---
     test/objects.js | 11 +++++++++++
     underscore.js   |  2 +-
     2 files changed, 12 insertions(+), 1 deletion(-)
    
    diff --git a/test/objects.js b/test/objects.js
    index 1745d8a3f..072cfb537 100644
    --- a/test/objects.js
    +++ b/test/objects.js
    @@ -481,6 +481,17 @@ $(document).ready(function() {
         ok(_.isRegExp(iRegExp), 'even from another frame');
       });
     
    +  test("objects: isFinite", function() {
    +    ok(!_.isFinite(undefined), 'undefined is not Finite');
    +    ok(!_.isFinite(null), 'null is not Finite');
    +    ok(!_.isFinite(Infinity), 'Infinity is not Finite');
    +    ok(!_.isFinite(-Infinity), '-Infinity is not Finite');
    +    ok(!_.isFinite('12'), 'Strings are not numbers');
    +    ok(_.isFinite(0), '0 is Finite');
    +    ok(_.isFinite(123), 'Ints are Finite');
    +    ok(_.isFinite(-12.44), 'Floats are Finite');
    +      });
    +
       test("objects: isNaN", function() {
         ok(!_.isNaN(undefined), 'undefined is not NaN');
         ok(!_.isNaN(null), 'null is not NaN');
    diff --git a/underscore.js b/underscore.js
    index 98ffea186..5060147e3 100644
    --- a/underscore.js
    +++ b/underscore.js
    @@ -814,7 +814,7 @@
     
       // Is a givin number finite?
       _.isFinite = function(obj) {
    -    return obj > -1/0 && obj < 1/0 && obj === -obj;
    +    return obj > -1/0 && obj < 1/0 && obj === +obj;
       };
     
       // Is the given value `NaN`?
    
    From 55b68fdabaa28688ac037530b842dcf4ccf19d2e Mon Sep 17 00:00:00 2001
    From: Raymond May Jr 
    Date: Sun, 25 Mar 2012 11:20:57 -0500
    Subject: [PATCH 40/66] formatting
    
    ---
     test/objects.js | 2 +-
     1 file changed, 1 insertion(+), 1 deletion(-)
    
    diff --git a/test/objects.js b/test/objects.js
    index 072cfb537..a5e3c7c62 100644
    --- a/test/objects.js
    +++ b/test/objects.js
    @@ -490,7 +490,7 @@ $(document).ready(function() {
         ok(_.isFinite(0), '0 is Finite');
         ok(_.isFinite(123), 'Ints are Finite');
         ok(_.isFinite(-12.44), 'Floats are Finite');
    -      });
    +  });
     
       test("objects: isNaN", function() {
         ok(!_.isNaN(undefined), 'undefined is not NaN');
    
    From bf6cb03519b1237c5d48039299bc3a6aa0134435 Mon Sep 17 00:00:00 2001
    From: Raymond May Jr 
    Date: Sun, 25 Mar 2012 14:10:35 -0500
    Subject: [PATCH 41/66] test against NaN
    
    ---
     test/objects.js | 1 +
     1 file changed, 1 insertion(+)
    
    diff --git a/test/objects.js b/test/objects.js
    index a5e3c7c62..630e2de30 100644
    --- a/test/objects.js
    +++ b/test/objects.js
    @@ -484,6 +484,7 @@ $(document).ready(function() {
       test("objects: isFinite", function() {
         ok(!_.isFinite(undefined), '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');
         ok(!_.isFinite(-Infinity), '-Infinity is not Finite');
         ok(!_.isFinite('12'), 'Strings are not numbers');
    
    From 0d935b0427be9dc30804aceb29938ba29c91472e Mon Sep 17 00:00:00 2001
    From: =?UTF-8?q?Mislav=20Marohnic=CC=81?= 
    Date: Mon, 26 Mar 2012 13:42:38 +0200
    Subject: [PATCH 42/66] have sidebar scroll natively on touch devices
    
    ---
     index.html | 1 +
     1 file changed, 1 insertion(+)
    
    diff --git a/index.html b/index.html
    index 40848c25c..95b786316 100644
    --- a/index.html
    +++ b/index.html
    @@ -23,6 +23,7 @@
           width: 200px;
           overflow-y: auto;
           overflow-x: hidden;
    +      -webkit-overflow-scrolling: touch;
           padding: 15px 0 30px 30px;
           border-right: 1px solid #bbb;
           box-shadow: 0 0 20px #ccc; -webkit-box-shadow: 0 0 20px #ccc; -moz-box-shadow: 0 0 20px #ccc;
    
    From 2c0ccf03ef1b5d0d54b267341f57fd7f8161b75c Mon Sep 17 00:00:00 2001
    From: Brad Dunbar 
    Date: Mon, 26 Mar 2012 13:37:06 -0400
    Subject: [PATCH 43/66] Documentation for `_.template(...).source`.
    
    ---
     index.html    | 14 +++++++++++++-
     underscore.js |  2 +-
     2 files changed, 14 insertions(+), 2 deletions(-)
    
    diff --git a/index.html b/index.html
    index 9b163c16b..99453e2ce 100644
    --- a/index.html
    +++ b/index.html
    @@ -1327,7 +1327,7 @@ 

    Utility Functions


    Compiles JavaScript templates into functions that can be evaluated for rendering. Useful for rendering complicated bits of HTML from JSON - data sources. Template functions can both interpolate variables, using
    + data sources. Template functions can both interpolate variables, using <%= … %>, as well as execute arbitrary JavaScript code, with <% … %>. If you wish to interpolate a value, and have it be HTML-escaped, use <%- … %> When you evaluate a template function, pass in a @@ -1381,6 +1381,18 @@

    Utility Functions

    template({name : "Mustache"}); => "Hello Mustache!"
    +

    + Precompiling your templates can be a big help when debugging errors you can't + reproduce. This is because precompiled templates can provide line numbers and + a stack trace, something that is not possible when compiling templates on the client. + template provides the source property on the compiled template + function for easy precompilation. +

    + +
    <script>
    +  JST.project = <%= _.template(jstText).source %>;
    +</script>
    +

    Chaining

    diff --git a/underscore.js b/underscore.js index 1649df208..44226a07c 100644 --- a/underscore.js +++ b/underscore.js @@ -960,7 +960,7 @@ var template = function(data) { return render.call(this, data, _); }; - template.source = 'function(obj, _){\n' + source + '\n}'; + template.source = 'function(obj){\n' + source + '\n}'; return template; }; From 4266d9180578f027a48655d8966fc771db6192e1 Mon Sep 17 00:00:00 2001 From: Raymond May Jr Date: Mon, 26 Mar 2012 13:00:25 -0500 Subject: [PATCH 44/66] test _.isFinite against new Number --- test/objects.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/test/objects.js b/test/objects.js index 630e2de30..62a74e731 100644 --- a/test/objects.js +++ b/test/objects.js @@ -488,6 +488,8 @@ $(document).ready(function() { ok(!_.isFinite(Infinity), 'Infinity is not Finite'); ok(!_.isFinite(-Infinity), '-Infinity is not Finite'); ok(!_.isFinite('12'), 'Strings are not numbers'); + var obj = new Number(5); + ok(_.isFinite(obj), 'Number instances can be finite'); ok(_.isFinite(0), '0 is Finite'); ok(_.isFinite(123), 'Ints are Finite'); ok(_.isFinite(-12.44), 'Floats are Finite'); From ee86820473b355af0a7fede78d7d948083a4ea46 Mon Sep 17 00:00:00 2001 From: Raymond May Jr Date: Mon, 26 Mar 2012 13:02:40 -0500 Subject: [PATCH 45/66] use _.isNumber for correctly working check --- underscore.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/underscore.js b/underscore.js index 5060147e3..eb279ae52 100644 --- a/underscore.js +++ b/underscore.js @@ -814,7 +814,7 @@ // Is a givin number finite? _.isFinite = function(obj) { - return obj > -1/0 && obj < 1/0 && obj === +obj; + return _.isNumber(obj) && isFinite(obj); }; // Is the given value `NaN`? From 7c9523784555f10d65f894a66e03f71b71ce96a0 Mon Sep 17 00:00:00 2001 From: Chris Leishman Date: Sat, 24 Mar 2012 12:26:03 -0700 Subject: [PATCH 46/66] Add _.restrict(source, *keys) Return a clone of the source with only the properties named in *keys (either stings or arrays containing strings). Especially useful for avoiding mass-assignment vulnerabilities. --- index.html | 13 +++++++++++++ test/objects.js | 10 ++++++++++ underscore.js | 10 ++++++++++ 3 files changed, 33 insertions(+) diff --git a/index.html b/index.html index 99453e2ce..9b679b578 100644 --- a/index.html +++ b/index.html @@ -1014,6 +1014,19 @@

    Object Functions

     _.extend({name : 'moe'}, {age : 50});
     => {name : 'moe', age : 50}
    +
    + +

    + restrict_.restrict(source, *keys) +
    + Return a clone of the source with only the properties with the + property names, or arrays of property names, provided in keys. +

    +
    +_.restrict({name : 'moe', age: 50, userid : 'moe1'}, 'name', 'age');
    +=> {name : 'moe', age : 50}
    +_.restrict({name : 'moe', age: 50, userid : 'moe1'}, ['name', 'age']);
    +=> {name : 'moe', age : 50}
     

    diff --git a/test/objects.js b/test/objects.js index 1745d8a3f..422e75754 100644 --- a/test/objects.js +++ b/test/objects.js @@ -41,6 +41,16 @@ $(document).ready(function() { equal(_.keys(result).join(''), 'ab', 'extend does not copy undefined values'); }); + test("objects: restrict", function() { + var result; + result = _.restrict({a:1, b:2, c:3}, 'a', 'c'); + ok(_.isEqual(result, {a:1, c:3}), 'can restrict properties to those named'); + result = _.restrict({a:1, b:2, c:3}, ['b', 'c']); + ok(_.isEqual(result, {b:2, c:3}), 'can restrict properties to those named in an array'); + result = _.restrict({a:1, b:2, c:3}, ['a'], 'b'); + ok(_.isEqual(result, {a:1, b:2}), 'can restrict properties to those named in mixed args'); + }); + test("objects: defaults", function() { var result; var options = {zero: 0, one: 1, empty: "", nan: NaN, string: "string"}; diff --git a/underscore.js b/underscore.js index 44226a07c..17549c5e8 100644 --- a/underscore.js +++ b/underscore.js @@ -645,6 +645,16 @@ return obj; }; + // Restrict a given object to the properties named + _.restrict = function(obj) { + if (obj !== Object(obj)) throw new TypeError('Invalid object'); + var dest = {}; + each(_.flatten(slice.call(arguments, 1)), function(prop) { + if (prop in obj) dest[prop] = obj[prop]; + }); + return dest; + }; + // Fill in a given object with default properties. _.defaults = function(obj) { each(slice.call(arguments, 1), function(source) { From 6b8a99ba395c6b53fdec24c8d134861b47a09d65 Mon Sep 17 00:00:00 2001 From: "Yi, EungJun" Date: Sat, 31 Mar 2012 02:27:49 +0900 Subject: [PATCH 47/66] Adding the ability to _.sortBy(list, 'property') --- test/collections.js | 4 ++++ underscore.js | 3 ++- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/test/collections.js b/test/collections.js index 18b242769..68e90246f 100644 --- a/test/collections.js +++ b/test/collections.js @@ -235,6 +235,10 @@ $(document).ready(function() { var people = [{name : 'curly', age : 50}, {name : 'moe', age : 30}]; people = _.sortBy(people, function(person){ return person.age; }); equal(_.pluck(people, 'name').join(', '), 'moe, curly', 'stooges sorted by age'); + + var list = ["one", "two", "three", "four", "five"]; + var sorted = _.sortBy(list, 'length'); + equal(sorted.join(' '), 'one two four five three', 'sorted by length'); }); test('collections: groupBy', function() { diff --git a/underscore.js b/underscore.js index 44226a07c..203b6d147 100644 --- a/underscore.js +++ b/underscore.js @@ -262,7 +262,8 @@ }; // Sort the object's values by a criterion produced by an iterator. - _.sortBy = function(obj, iterator, context) { + _.sortBy = function(obj, val, context) { + var iterator = _.isFunction(val) ? val : function(obj) { return obj[val]; }; return _.pluck(_.map(obj, function(value, index, list) { return { value : value, From e4c4300a064b9bf21bfcf112088fe9cd0f46a049 Mon Sep 17 00:00:00 2001 From: Chris Baynes Date: Sat, 31 Mar 2012 11:05:47 +0200 Subject: [PATCH 48/66] Allow sortBy to handle undefined values. --- underscore.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/underscore.js b/underscore.js index 44226a07c..00ca13d4a 100644 --- a/underscore.js +++ b/underscore.js @@ -270,6 +270,8 @@ }; }).sort(function(left, right) { var a = left.criteria, b = right.criteria; + if (_.isUndefined(a)) return 1; + if (_.isUndefined(b)) return -1; return a < b ? -1 : a > b ? 1 : 0; }), 'value'); }; From db7d198a55c2af27785ee0b80d1ea5b84581b11f Mon Sep 17 00:00:00 2001 From: Jeremy Ashkenas Date: Mon, 2 Apr 2012 13:05:52 -0400 Subject: [PATCH 49/66] Fixes #538 -- confusing wording around chaining. --- index.html | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/index.html b/index.html index 9165f1428..e8d310c76 100644 --- a/index.html +++ b/index.html @@ -1382,8 +1382,7 @@

    Chaining

    _([1, 2, 3]).map(function(n){ return n * 2; });

    - Using the object-oriented style allows you to chain together methods. Calling - chain on a wrapped object will cause all future method calls to + Calling chain on a wrapped object will cause all future method calls to return wrapped objects as well. When you've finished the computation, use value to retrieve the final value. Here's an example of chaining together a map/flatten/reduce, in order to get the word count of @@ -1534,7 +1533,8 @@

    Change Log

    1.2.4Jan. 4, 2012
    • - You now can (and probably should) write _.chain(list) + You now can (and probably should, as it's simpler) + write _.chain(list) instead of _(list).chain().
    • From 6f50830b25c41a22c1d5a165bc13a71a65b9d934 Mon Sep 17 00:00:00 2001 From: Jeremy Ashkenas Date: Mon, 2 Apr 2012 13:05:52 -0400 Subject: [PATCH 50/66] Fixes #538 -- confusing wording around chaining. --- index.html | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/index.html b/index.html index 95b786316..32babe334 100644 --- a/index.html +++ b/index.html @@ -1368,8 +1368,7 @@

      Chaining

      _([1, 2, 3]).map(function(n){ return n * 2; });

    - Using the object-oriented style allows you to chain together methods. Calling - chain on a wrapped object will cause all future method calls to + Calling chain on a wrapped object will cause all future method calls to return wrapped objects as well. When you've finished the computation, use value to retrieve the final value. Here's an example of chaining together a map/flatten/reduce, in order to get the word count of @@ -1520,7 +1519,8 @@

    Change Log

    1.2.4Jan. 4, 2012
    • - You now can (and probably should) write _.chain(list) + You now can (and probably should, as it's simpler) + write _.chain(list) instead of _(list).chain().
    • From d60489501843515e714a21153a579471e370bbfb Mon Sep 17 00:00:00 2001 From: Jeremy Ashkenas Date: Mon, 2 Apr 2012 13:16:58 -0400 Subject: [PATCH 51/66] revisions to #534, sortBy(undefined) --- test/collections.js | 3 +++ underscore.js | 4 ++-- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/test/collections.js b/test/collections.js index 18b242769..fb457f605 100644 --- a/test/collections.js +++ b/test/collections.js @@ -235,6 +235,9 @@ $(document).ready(function() { var people = [{name : 'curly', age : 50}, {name : 'moe', age : 30}]; people = _.sortBy(people, function(person){ return person.age; }); equal(_.pluck(people, 'name').join(', '), 'moe, curly', 'stooges sorted by age'); + + var list = [undefined, 4, 1, undefined, 3, 2]; + equal(_.sortBy(list, _.identity).join(','), '1,2,3,4,,', 'sortBy with undefined values'); }); test('collections: groupBy', function() { diff --git a/underscore.js b/underscore.js index 00ca13d4a..e3db3646d 100644 --- a/underscore.js +++ b/underscore.js @@ -270,8 +270,8 @@ }; }).sort(function(left, right) { var a = left.criteria, b = right.criteria; - if (_.isUndefined(a)) return 1; - if (_.isUndefined(b)) return -1; + if (a === void 0) return 1; + if (b === void 0) return -1; return a < b ? -1 : a > b ? 1 : 0; }), 'value'); }; From 719cd24aa9c40990294d607ce2f5bad65b7aac7c Mon Sep 17 00:00:00 2001 From: Jeremy Ashkenas Date: Mon, 2 Apr 2012 15:01:26 -0400 Subject: [PATCH 52/66] adding an initial implementation of a 'pick' function. #523 --- index.html | 13 ++++++------- test/objects.js | 12 ++++++------ underscore.js | 13 ++++++------- 3 files changed, 18 insertions(+), 20 deletions(-) diff --git a/index.html b/index.html index 7cabf46e6..f38ffa8ab 100644 --- a/index.html +++ b/index.html @@ -214,6 +214,7 @@
    • - values
    • - functions
    • - extend
    • +
    • - pick
    • - defaults
    • - clone
    • - tap
    • @@ -1016,16 +1017,14 @@

      Object Functions

      => {name : 'moe', age : 50}
    -

    - restrict_.restrict(source, *keys) +

    + pick_.pick(object, *keys)
    - Return a clone of the source with only the properties with the - property names, or arrays of property names, provided in keys. + Return a copy of the object, filtered to only have values for + the whitelisted keys (or array of valid keys).

    -_.restrict({name : 'moe', age: 50, userid : 'moe1'}, 'name', 'age');
    -=> {name : 'moe', age : 50}
    -_.restrict({name : 'moe', age: 50, userid : 'moe1'}, ['name', 'age']);
    +_.pick({name : 'moe', age: 50, userid : 'moe1'}, 'name', 'age');
     => {name : 'moe', age : 50}
     
    diff --git a/test/objects.js b/test/objects.js index 422e75754..8fb3d1b9d 100644 --- a/test/objects.js +++ b/test/objects.js @@ -41,13 +41,13 @@ $(document).ready(function() { equal(_.keys(result).join(''), 'ab', 'extend does not copy undefined values'); }); - test("objects: restrict", function() { + test("objects: pick", function() { var result; - result = _.restrict({a:1, b:2, c:3}, 'a', 'c'); + result = _.pick({a:1, b:2, c:3}, 'a', 'c'); ok(_.isEqual(result, {a:1, c:3}), 'can restrict properties to those named'); - result = _.restrict({a:1, b:2, c:3}, ['b', 'c']); + result = _.pick({a:1, b:2, c:3}, ['b', 'c']); ok(_.isEqual(result, {b:2, c:3}), 'can restrict properties to those named in an array'); - result = _.restrict({a:1, b:2, c:3}, ['a'], 'b'); + result = _.pick({a:1, b:2, c:3}, ['a'], 'b'); ok(_.isEqual(result, {a:1, b:2}), 'can restrict properties to those named in mixed args'); }); @@ -178,7 +178,7 @@ $(document).ready(function() { // Arrays with primitive and object values. ok(_.isEqual([1, "Larry", true], [1, "Larry", true]), "Arrays containing identical primitives are equal"); - ok(_.isEqual([/Moe/g, new Date(2009, 9, 25)], [/Moe/g, new Date(2009, 9, 25)]), "Arrays containing equivalent elements are equal"); + ok(_.isEqual([(/Moe/g), new Date(2009, 9, 25)], [(/Moe/g), new Date(2009, 9, 25)]), "Arrays containing equivalent elements are equal"); // Multi-dimensional arrays. var a = [new Number(47), false, "Larry", /Moe/, new Date(2009, 11, 13), ['running', 'biking', new String('programming')], {a: 47}]; @@ -346,7 +346,7 @@ $(document).ready(function() { Date.prototype.isEqual = function(that) { var this_date_components = this.toJSON(); var that_date_components = (that instanceof Date) ? that.toJSON() : that; - delete this_date_components['_type']; delete that_date_components['_type'] + delete this_date_components['_type']; delete that_date_components['_type']; return _.isEqual(this_date_components, that_date_components); }; diff --git a/underscore.js b/underscore.js index 12423c7e5..cb2d424de 100644 --- a/underscore.js +++ b/underscore.js @@ -648,14 +648,13 @@ return obj; }; - // Restrict a given object to the properties named - _.restrict = function(obj) { - if (obj !== Object(obj)) throw new TypeError('Invalid object'); - var dest = {}; - each(_.flatten(slice.call(arguments, 1)), function(prop) { - if (prop in obj) dest[prop] = obj[prop]; + // Return a copy of the object only containing the whitelisted properties. + _.pick = function(obj) { + var result = {}; + each(_.flatten(slice.call(arguments, 1)), function(key) { + if (key in obj) result[key] = obj[key]; }); - return dest; + return result; }; // Fill in a given object with default properties. From 6f5489fcabd510ca5b0e62ae04e549775da32e2d Mon Sep 17 00:00:00 2001 From: Jeremy Ashkenas Date: Mon, 2 Apr 2012 15:07:56 -0400 Subject: [PATCH 53/66] comment tweak --- underscore.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/underscore.js b/underscore.js index 4276ece3e..168bcc209 100644 --- a/underscore.js +++ b/underscore.js @@ -824,7 +824,7 @@ return toString.call(obj) == '[object Number]'; }; - // Is a givin number finite? + // Is a given object a finite number? _.isFinite = function(obj) { return _.isNumber(obj) && isFinite(obj); }; From 4620eb7b617d3be2de3917e8e0c8ca78be0fab36 Mon Sep 17 00:00:00 2001 From: Jeremy Ashkenas Date: Mon, 2 Apr 2012 15:30:12 -0400 Subject: [PATCH 54/66] reordering the patch for #504 --- underscore.js | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/underscore.js b/underscore.js index 12ab9ed68..636865628 100644 --- a/underscore.js +++ b/underscore.js @@ -302,12 +302,12 @@ }; // Safely convert anything iterable into a real, live array. - _.toArray = function(iterable) { - if (!iterable) return []; - if (iterable.toArray && _.isFunction(iterable.toArray)) return iterable.toArray(); - if (_.isArray(iterable)) return slice.call(iterable); - if (_.isArguments(iterable)) return slice.call(iterable); - return _.values(iterable); + _.toArray = function(obj) { + if (!obj) return []; + if (_.isArray(obj)) return slice.call(obj); + if (_.isArguments(obj)) return slice.call(obj); + if (obj.toArray && _.isFunction(obj.toArray)) return obj.toArray(); + return _.values(obj); }; // Return the number of elements in an object. From b52908f78fc6b51a6c5ec28b7dc61092cfd0a4df Mon Sep 17 00:00:00 2001 From: Jeremy Ashkenas Date: Mon, 2 Apr 2012 15:50:31 -0400 Subject: [PATCH 55/66] Fixes #491 - IE9 / Opera 11 test case. --- test/objects.js | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/test/objects.js b/test/objects.js index 2ee0a514d..e2803b21c 100644 --- a/test/objects.js +++ b/test/objects.js @@ -3,16 +3,15 @@ $(document).ready(function() { module("Objects"); test("objects: keys", function() { - var exception = /object/; equal(_.keys({one : 1, two : 2}).join(', '), '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; equal(_.keys(a).join(', '), '1', 'is not fooled by sparse arrays; see issue #95'); - raises(function() { _.keys(null); }, exception, 'throws an error for `null` values'); - raises(function() { _.keys(void 0); }, exception, 'throws an error for `undefined` values'); - raises(function() { _.keys(1); }, exception, 'throws an error for number primitives'); - raises(function() { _.keys('a'); }, exception, 'throws an error for string primitives'); - raises(function() { _.keys(true); }, exception, 'throws an error for boolean primitives'); + raises(function() { _.keys(null); }, TypeError, 'throws an error for `null` values'); + raises(function() { _.keys(void 0); }, TypeError, 'throws an error for `undefined` values'); + raises(function() { _.keys(1); }, TypeError, 'throws an error for number primitives'); + raises(function() { _.keys('a'); }, TypeError, 'throws an error for string primitives'); + raises(function() { _.keys(true); }, TypeError, 'throws an error for boolean primitives'); }); test("objects: values", function() { From 1366d903332fc2af1560c2c45b4fac5e6c75719b Mon Sep 17 00:00:00 2001 From: Jeremy Ashkenas Date: Mon, 2 Apr 2012 16:11:51 -0400 Subject: [PATCH 56/66] default timeout'd scope is null / global. Fixes #484 --- underscore.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/underscore.js b/underscore.js index 458ecd324..7d516cac7 100644 --- a/underscore.js +++ b/underscore.js @@ -519,7 +519,7 @@ // it with the arguments supplied. _.delay = function(func, wait) { var args = slice.call(arguments, 2); - return setTimeout(function(){ return func.apply(func, args); }, wait); + return setTimeout(function(){ return func.apply(null, args); }, wait); }; // Defers a function, scheduling it to run after the current call stack has From 73e6e87858fdbfa32fe8a41684be3ab6d01ae4d4 Mon Sep 17 00:00:00 2001 From: Jeremy Ashkenas Date: Mon, 2 Apr 2012 16:32:53 -0400 Subject: [PATCH 57/66] Fixes #465 -- throttled functions return their value when they actually run. --- test/functions.js | 11 +++++++---- underscore.js | 5 +++-- 2 files changed, 10 insertions(+), 6 deletions(-) diff --git a/test/functions.js b/test/functions.js index 086233960..f8e5dc5c8 100644 --- a/test/functions.js +++ b/test/functions.js @@ -117,12 +117,15 @@ $(document).ready(function() { _.delay(function(){ equal(value, 6, "updated to latest value"); start(); }, 400); }); - asyncTest("functions: throttle once", 1, function() { + asyncTest("functions: throttle once", 2, function() { var counter = 0; - var incr = function(){ counter++; }; + var incr = function(){ return ++counter; }; var throttledIncr = _.throttle(incr, 100); - throttledIncr(); - _.delay(function(){ equal(counter, 1, "incr was called once"); start(); }, 220); + var result = throttledIncr(); + _.delay(function(){ + equal(result, 1, "throttled functions return their value"); + equal(counter, 1, "incr was called once"); start(); + }, 220); }); asyncTest("functions: throttle twice", 1, function() { diff --git a/underscore.js b/underscore.js index 7d516cac7..72dc8e9f7 100644 --- a/underscore.js +++ b/underscore.js @@ -531,7 +531,7 @@ // Returns a function, that, when invoked, will only be triggered at most once // during a given window of time. _.throttle = function(func, wait) { - var context, args, timeout, throttling, more; + var context, args, timeout, throttling, more, result; var whenDone = _.debounce(function(){ more = throttling = false; }, wait); return function() { context = this; args = arguments; @@ -544,10 +544,11 @@ if (throttling) { more = true; } else { - func.apply(context, args); + result = func.apply(context, args); } whenDone(); throttling = true; + return result; }; }; From a40001a358a185b4da8a2836381ce5a5a5d29eb6 Mon Sep 17 00:00:00 2001 From: Jakub Wieczorek Date: Tue, 3 Apr 2012 15:23:38 +0200 Subject: [PATCH 58/66] Remove the unnecessary branch in _.shuffle() The non-zero case works properly for 0 as well. --- test/speed.js | 4 ++++ underscore.js | 10 +++------- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/test/speed.js b/test/speed.js index 86663a237..82033c51f 100644 --- a/test/speed.js +++ b/test/speed.js @@ -67,4 +67,8 @@ return _.range(1000); }); + JSLitmus.test('_.shuffle()', function() { + return _.shuffle(numbers); + }); + })(); \ No newline at end of file diff --git a/underscore.js b/underscore.js index 72dc8e9f7..3b6e4b990 100644 --- a/underscore.js +++ b/underscore.js @@ -250,13 +250,9 @@ _.shuffle = function(obj) { var shuffled = [], rand; each(obj, function(value, index, list) { - if (index == 0) { - shuffled[0] = value; - } else { - rand = Math.floor(Math.random() * (index + 1)); - shuffled[index] = shuffled[rand]; - shuffled[rand] = value; - } + rand = Math.floor(Math.random() * (index + 1)); + shuffled[index] = shuffled[rand]; + shuffled[rand] = value; }); return shuffled; }; From 8cfb076c9b47c18d15c5cefc5652ccb9eb6119b4 Mon Sep 17 00:00:00 2001 From: Jeremy Ashkenas Date: Tue, 3 Apr 2012 11:49:31 -0400 Subject: [PATCH 59/66] removing added speed test --- test/speed.js | 4 ---- 1 file changed, 4 deletions(-) diff --git a/test/speed.js b/test/speed.js index 82033c51f..86663a237 100644 --- a/test/speed.js +++ b/test/speed.js @@ -67,8 +67,4 @@ return _.range(1000); }); - JSLitmus.test('_.shuffle()', function() { - return _.shuffle(numbers); - }); - })(); \ No newline at end of file From b04b813f025913fb13a91419062a4279bab80335 Mon Sep 17 00:00:00 2001 From: Brad Dunbar Date: Tue, 3 Apr 2012 10:57:39 -0700 Subject: [PATCH 60/66] Add varname to _.templateSettings. * Leave out the with statement when using varname. * Rename source to compiled. * Comments, comments, comments. --- index.html | 23 +++++++++++++++++----- test/utility.js | 11 ++++++++++- underscore.js | 51 +++++++++++++++++++++++++++++++------------------ 3 files changed, 60 insertions(+), 25 deletions(-) diff --git a/index.html b/index.html index f38ffa8ab..1872377d9 100644 --- a/index.html +++ b/index.html @@ -3,7 +3,7 @@ - + Underscore.js