diff --git a/index.html b/index.html
index db3ee81b0..11f706290 100644
--- a/index.html
+++ b/index.html
@@ -33,9 +33,14 @@
h1, h2, h3, h4, h5, h6 {
margin-top: 40px;
}
- b.method_name {
+ b.header {
font-size: 18px;
}
+ span.alias {
+ font-size: 14px;
+ font-style: italic;
+ margin-left: 20px;
+ }
table, tr, td {
margin: 0; padding: 0;
}
@@ -76,7 +81,7 @@
- Underscore provides 42-odd functions that support both the usual
+ Underscore provides 44-odd functions that support both the usual
functional suspects: map, select, invoke —
as well as more specialized helpers: function binding, javascript
templating, deep equality testing, and so on. It delegates to built-in
@@ -102,11 +107,11 @@
@@ -118,7 +123,7 @@
Collections
each, map,
- inject, detect, select, reject, all,
+ reduce, detect, select, reject, all,
any, include, invoke, pluck, max,
min, sortBy, sortedIndex, toArray,
size
@@ -129,14 +134,15 @@
first, last,
compact, flatten, without, uniq,
- intersect, zip, indexOf
+ intersect, zip, indexOf,
+ lastIndexOf
Functions
bind, bindAll, delay,
- defer, wrap
+ defer, wrap, compose
@@ -160,7 +166,8 @@
Collection Functions (Arrays or Objects)
- each_.each(list, iterator, [context])
+ _.each(list, iterator, [context])
+ Alias: forEach
Iterates over a list of elements, yielding each in turn to an iterator
function. The iterator is bound to the context object, if one is
@@ -174,7 +181,7 @@ _.each([1, 2, 3], function(num){ alert(num); });
=> alerts each number in turn...
- map_.map(list, iterator, [context])
+ _.map(list, iterator, [context])
Produces a new array of values by mapping each value in list
through a transformation function (iterator). If the native
@@ -184,21 +191,22 @@ _.each([1, 2, 3], function(num){ alert(num); });
_.map([1, 2, 3], function(num){ return num * 3 });
=> [3, 6, 9]
-
- inject_.inject(list, memo, iterator, [context])
+
+ _.reduce(list, memo, iterator, [context])
+ Alias: inject
- Also known as reduce and foldl, inject reduces a
+ Also known as inject and foldl, reduce boils down a
list of values into a single value. Memo is the initial state
of the reduction, and each successive step of it should be returned by
iterator.
-var sum = _.inject([1, 2, 3], 0, function(memo, num){ return memo + num });
+var sum = _.reduce([1, 2, 3], 0, function(memo, num){ return memo + num });
=> 6
- detect_.detect(list, iterator, [context])
+ _.detect(list, iterator, [context])
Looks through each value in the list, returning the first one that
passes a truth test (iterator). The function returns as
@@ -211,7 +219,8 @@ var even = _.detect([1, 2, 3, 4, 5, 6], function(num){ return num % 2 == 0; });
- select_.select(list, iterator, [context])
+ _.select(list, iterator, [context])
+ Alias: filter
Looks through each value in the list, returning an array of all
the values that pass a truth test (iterator). Delegates to the
@@ -223,7 +232,7 @@ var evens = _.select([1, 2, 3, 4, 5, 6], function(num){ return num % 2 == 0; });
- reject_.reject(list, iterator, [context])
+ _.reject(list, iterator, [context])
Returns the values in list without the elements that the truth
test (iterator) passes. The opposite of select.
@@ -234,7 +243,8 @@ var odds = _.reject([1, 2, 3, 4, 5, 6], function(num){ return num % 2 == 0; });
- all_.all(list, [iterator], [context])
+ _.all(list, [iterator], [context])
+ Alias: every
Returns true if all of the values in the list pass the iterator
truth test. If an iterator is not provided, the truthy value of
@@ -247,7 +257,8 @@ _.all([true, 1, null, 'yes']);
- any_.any(list, [iterator], [context])
+ _.any(list, [iterator], [context])
+ Alias: some
Returns true if any of the values in the list pass the
iterator truth test. Short-circuits and stops traversing the list
@@ -260,7 +271,7 @@ _.any([null, 0, 'yes', false]);
- include_.include(list, value)
+ _.include(list, value)
Returns true if the value is present in the list, using
=== to test equality. Uses indexOf internally, if list
@@ -272,7 +283,7 @@ _.include([1, 2, 3], 3);
- invoke_.invoke(list, methodName, [*arguments])
+ _.invoke(list, methodName, [*arguments])
Calls the method named by methodName on each value in the list.
Any extra arguments passed to invoke will be forwarded on to the
@@ -284,7 +295,7 @@ _.invoke([[5, 1, 7], [3, 2, 1]], 'sort');
- pluck_.pluck(list, propertyName)
+ _.pluck(list, propertyName)
An optimized version of what is perhaps the most common use-case for
map: returning a list of property values.
@@ -296,7 +307,7 @@ _.pluck(stooges, 'name');
- max_.max(list, [iterator], [context])
+ _.max(list, [iterator], [context])
Returns the maximum value in list. If iterator is passed,
it will be used on each value to generate the criterion by which the
@@ -309,7 +320,7 @@ _.max(stooges, function(stooge){ return stooge.age; });
- min_.min(list, [iterator], [context])
+ _.min(list, [iterator], [context])
Returns the minimum value in list. If iterator is passed,
it will be used on each value to generate the criterion by which the
@@ -322,7 +333,7 @@ _.min(numbers);
- sortBy_.sortBy(list, iterator, [context])
+ _.sortBy(list, iterator, [context])
Returns a sorted list, ranked by the results of running each
value through iterator.
@@ -333,7 +344,7 @@ _.sortBy([1, 2, 3, 4, 5, 6], function(num){ return Math.sin(num); });
- sortedIndex_.sortedIndex(list, value, [iterator])
+ _.sortedIndex(list, value, [iterator])
Uses a binary search to determine the index at which the value
should be inserted into the list in order to maintain the list's
@@ -346,7 +357,7 @@ _.sortedIndex([10, 20, 30, 40, 50], 35);
- toArray_.toArray(list)
+ _.toArray(list)
Converts the list (anything that can be iterated over), into a
real Array. Useful for transmuting the arguments object.
@@ -357,7 +368,7 @@ _.sortedIndex([10, 20, 30, 40, 50], 35);
- size_.size(list)
+ _.size(list)
Return the number of values in the list.
@@ -369,7 +380,7 @@ _.size({one : 1, two : 2, three : 3});
Array Functions
- first_.first(array)
+ _.first(array)
Convenience to return the first element of an array (identical to array[0]).
@@ -379,7 +390,7 @@ _.first([3, 2, 1]);
- last_.last(array)
+ _.last(array)
Returns the last element of an array.
@@ -389,7 +400,7 @@ _.last([3, 2, 1]);
- compact_.compact(array)
+ _.compact(array)
Returns a copy of the array with all falsy values removed.
In Javascript, false, null, 0, "",
@@ -401,7 +412,7 @@ _.compact([0, 1, false, 2, '', 3]);
- flatten_.flatten(array)
+ _.flatten(array)
Flattens a nested array (the nesting can be to any depth).
@@ -411,7 +422,7 @@ _.flatten([1, [2], [3, [[[4]]]]]);
- without_.without(array, [*values])
+ _.without(array, [*values])
Returns a copy of the array with all instances of the values
removed. === is used for the equality test.
@@ -422,7 +433,7 @@ _.without([1, 2, 1, 0, 3, 1, 4], 0, 1);
- uniq_.uniq(array, [isSorted])
+ _.uniq(array, [isSorted])
Produces a duplicate-free version of the array, using === to test
object equality. If you know in advance that the array is sorted,
@@ -434,7 +445,7 @@ _.uniq([1, 2, 1, 3, 1, 4]);
- intersect_.intersect(*arrays)
+ _.intersect(*arrays)
Computes the list of values that are the intersection of all the arrays.
Each value in the result is present in each of the arrays.
@@ -445,7 +456,7 @@ _.intersect([1, 2, 3], [101, 2, 1, 10], [2, 1]);
- zip_.zip(*arrays)
+ _.zip(*arrays)
Merges together the values of each of the arrays with the
values at the corresponding position. Useful when you have separate
@@ -457,7 +468,7 @@ _.zip(['moe', 'larry', 'curly'], [30, 40, 50], [true, false, false]);
- indexOf_.indexOf(array, value)
+ _.indexOf(array, value)
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
@@ -466,12 +477,24 @@ _.zip(['moe', 'larry', 'curly'], [30, 40, 50], [true, false, false]);
_.indexOf([1, 2, 3], 2);
=> 1
+
+
+
+ _.lastIndexOf(array, value)
+
+ Returns the index of the last occurrence of value in the array,
+ or -1 if value is not present. Uses the native lastIndexOf
+ function if possible.
+
+
+_.lastIndexOf([1, 2, 3, 1, 2, 3], 2);
+=> 4
Function (uh, ahem) Functions
- bind_.bind(function, context, [*arguments])
+ _.bind(function, context, [*arguments])
Bind a function to a context object, meaning that whenever
the function is called, the value of this will be the context.
@@ -486,7 +509,7 @@ func();
- bindAll_.bindAll(*methodNames, context)
+ _.bindAll(*methodNames, context)
Binds a number of methods on the context object, specified by
methodNames, to be run in the context of that object whenever they
@@ -506,7 +529,7 @@ jQuery('#underscore_button').bind('click', buttonView.onClick);
- delay_.delay(function, wait, [*arguments])
+ _.delay(function, wait, [*arguments])
Much like setTimeout, invokes function after wait
milliseconds. If you pass the optional arguments, they will be
@@ -519,7 +542,7 @@ _.delay(log, 1000, 'logged later');
- defer_.defer(function)
+ _.defer(function)
Defers invoking the function until the current call stack has cleared,
similar to using setTimeout with a delay of 0. Useful for performing
@@ -532,7 +555,7 @@ _.defer(function(){ alert('deferred'); });
- wrap_.wrap(function, wrapper)
+ _.wrap(function, wrapper)
Wraps the first function inside of the wrapper function,
passing it as the first argument. This allows the wrapper to
@@ -545,13 +568,29 @@ hello = _.wrap(hello, function(func) {
return "before, " + func("moe") + ", after";
});
hello();
-=> before, hello: moe, after
+=> 'before, hello: moe, after'
+
+
+
+ _.compose(*functions)
+
+ Returns the composition of a list of functions, where each function
+ consumes the return value of the function that follows. In math terms,
+ composing the functions f(), g(), and h() produces
+ f(g(h())).
+
+
+var greet = function(name){ return "hi: " + name; };
+var exclaim = function(statement){ return statement + "!"; };
+var welcome = _.compose(greet, exclaim);
+welcome('moe');
+=> 'hi: moe!'
Object Functions
- keys_.keys(object)
+ _.keys(object)
Retrieve all the names of the object's properties.
@@ -561,7 +600,7 @@ _.keys({one : 1, two : 2, three : 3});
- values_.values(object)
+ _.values(object)
Return all of the values of the object's properties.
@@ -571,7 +610,7 @@ _.values({one : 1, two : 2, three : 3});
- extend_.extend(destination, source)
+ _.extend(destination, source)
Copy all of the properties in the source object over to the
destination object.
@@ -582,7 +621,7 @@ _.extend({name : 'moe'}, {age : 50});
- clone_.clone(object)
+ _.clone(object)
Create a shallow-copied clone of the object. Any nested objects
or arrays will be copied by reference, not duplicated.
@@ -593,7 +632,7 @@ _.clone({name : 'moe'});
- isEqual_.isEqual(object, other)
+ _.isEqual(object, other)
Performs an optimized deep comparison between the two objects, to determine
if they should be considered equal.
@@ -608,7 +647,7 @@ _.isEqual(moe, clone);
- isElement_.isElement(object)
+ _.isElement(object)
Returns true if object is a DOM element.
@@ -618,7 +657,7 @@ _.isElement(jQuery('body')[0]);
- isArray_.isArray(object)
+ _.isArray(object)
Returns true if object is an Array.
@@ -630,7 +669,7 @@ _.isArray([1,2,3]);
- isFunction_.isFunction(object)
+ _.isFunction(object)
Returns true if object is a Function.
@@ -640,7 +679,7 @@ _.isFunction(alert);
- isUndefined_.isUndefined(variable)
+ _.isUndefined(variable)
Returns true if variable is undefined.
@@ -652,7 +691,7 @@ _.isUndefined(window.missingVariable);
Utility Functions
- noConflict_.noConflict()
+ _.noConflict()
Give control of the "_" variable back to its previous owner. Returns
a reference to the Underscore object.
@@ -661,7 +700,7 @@ _.isUndefined(window.missingVariable);
var underscore = _.noConflict();
- uniqueId_.uniqueId([prefix])
+ _.uniqueId([prefix])
Generate a globally-unique id for client-side models or DOM elements
that need one. If prefix is passed, the id will be appended to it.
@@ -672,7 +711,7 @@ _.uniqueId('contact_');
- template_.template(templateString, [context])
+ _.template(templateString, [context])
Compiles Javascript templates into functions that can be evaluated
for rendering. Useful for rendering complicated bits of HTML from JSON
@@ -694,6 +733,26 @@ _.template(list, {people : ['moe', 'curly', 'larry']});
=> "<li>moe</li><li>curly</li><li>larry</li>"
+
Change Log
+
+
+
+ Added compose and lastIndexOf, renamed inject to
+ reduce, added aliases for inject, filter,
+ every, some, and forEach.
+
+
+
+
+ Added noConflict, so that the "Underscore" object can be assigned to
+ other variables.
+
+
+
+
+ Initial release of Underscore.js.
+
+
diff --git a/package.json b/package.json
new file mode 100644
index 000000000..0f55aaa87
--- /dev/null
+++ b/package.json
@@ -0,0 +1,12 @@
+// ServerJS package specification.
+{
+ "name": "underscore",
+ "description": "Functional programming aid for Javascript. Works well with jQuery.",
+ "url": "http://documentcloud.github.com/underscore/",
+ "keywords": ["util", "functional", "server", "client", "browser"],
+ "author": "Jeremy Ashkenas ",
+ "maintainer": "Kris Kowal ",
+ "contributors": [],
+ "dependencies": [],
+ "lib", "."
+}
diff --git a/test/arrays.js b/test/arrays.js
index e59397996..dd54c7266 100644
--- a/test/arrays.js
+++ b/test/arrays.js
@@ -49,4 +49,11 @@ $(document).ready(function() {
equals(_.indexOf(numbers, 2), 1, 'can compute indexOf, even without the native function');
});
+ 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');
+ });
+
});
diff --git a/test/collections.js b/test/collections.js
index bed210ac2..f97e6e520 100644
--- a/test/collections.js
+++ b/test/collections.js
@@ -14,6 +14,10 @@ $(document).ready(function() {
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');
+
+ answers = [];
+ _.forEach([1, 2, 3], function(num){ answers.push(num); });
+ equals(answers.join(', '), '1, 2, 3', 'aliased as "forEach"');
});
test('collections: map', function() {
@@ -24,9 +28,12 @@ $(document).ready(function() {
equals(tripled.join(', '), '3, 6, 9', 'tripled numbers with context');
});
- test('collections: inject', function() {
- var sum = _.inject([1,2,3], 0, function(sum, num){ return sum + num; });
+ test('collections: reduce', function() {
+ var sum = _.reduce([1, 2, 3], 0, function(sum, num){ return sum + num; });
equals(sum, 6, 'can sum up an array');
+
+ sum = _.inject([1, 2, 3], 0, function(sum, num){ return sum + num; });
+ equals(sum, 6, 'aliased as "inject"');
});
test('collections: detect', function() {
@@ -35,12 +42,15 @@ $(document).ready(function() {
});
test('collections: select', function() {
- var evens = _.select([1,2,3,4,5,6], function(num){ return num % 2 == 0; });
+ 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');
+
+ evens = _.filter([1, 2, 3, 4, 5, 6], function(num){ return num % 2 == 0; });
+ equals(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; });
+ 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');
});
@@ -50,6 +60,7 @@ $(document).ready(function() {
ok(!_.all([true, false, true]), 'one false value');
ok(_.all([0, 10, 28], function(num){ return num % 2 == 0; }), 'even numbers');
ok(!_.all([0, 11, 28], function(num){ return num % 2 == 0; }), 'an odd number');
+ ok(_.every([true, true, true]), 'aliased as "every"');
});
test('collections: any', function() {
@@ -58,6 +69,7 @@ $(document).ready(function() {
ok(_.any([false, false, true]), 'one true value');
ok(!_.any([1, 11, 29], function(num){ return num % 2 == 0; }), 'all odd numbers');
ok(_.any([1, 10, 29], function(num){ return num % 2 == 0; }), 'an even number');
+ ok(_.some([false, false, true]), 'aliased as "some"');
});
test('collections: include', function() {
diff --git a/test/functions.js b/test/functions.js
index 039f7a746..9e401e4c7 100644
--- a/test/functions.js
+++ b/test/functions.js
@@ -48,4 +48,14 @@ $(document).ready(function() {
equals(backwards('moe'), 'hi: moe eom', 'wrapped the saluation function');
});
+ test("functions: compose", 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');
+
+ composed = _.compose(greet, exclaim);
+ equals(composed('moe'), 'hi: moe!', 'in this case, the functions are also commutative');
+ });
+
});
diff --git a/test/utility.js b/test/utility.js
index 9a8eb516f..af9680bb5 100644
--- a/test/utility.js
+++ b/test/utility.js
@@ -5,6 +5,8 @@ $(document).ready(function() {
test("utility: noConflict", 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');
window._ = underscore;
});
diff --git a/underscore-min.js b/underscore-min.js
index 096ff0aac..98d0437b5 100644
--- a/underscore-min.js
+++ b/underscore-min.js
@@ -1 +1 @@
-window.Underscore={VERSION:"0.1.1",PREVIOUS_UNDERSCORE:window._,each:function(c,f,a){var g=0;try{if(c.forEach){c.forEach(f,a)}else{if(c.length){for(var d=0;d=a.computed){a={value:g,computed:f}}});return a.value},min:function(d,c,b){if(!c&&_.isArray(d)){return Math.min.apply(Math,d)}var a;_.each(d,function(g,e){var f=c?c.call(b,g,e):g;if(a==null||fd?1:0}),"value")},sortedIndex:function(f,e,c){c=c||function(g){return g};var a=0,d=f.length;while(a>1;c(f[b])=0})})},zip:function(){var a=_.toArray(arguments);var d=_.max(_.pluck(a,"length"));var c=new Array(d);for(var b=0;b)[^\t]*)'/g,"$1\r").replace(/\t=(.*?)%>/g,"',$1,'").split("\t").join("');").split("%>").join("p.push('").split("\r").join("\\'")+"');}return p.join('');");return b?a(b):a}};window._=Underscore;
\ No newline at end of file
+(function(){var a=(typeof window!="undefined")?window:exports;var c=a._;var b=a._={};b.VERSION="0.2.0";b.each=function(g,j,d){var k=0;try{if(g.forEach){g.forEach(j,d)}else{if(g.length){for(var h=0;h=d.computed){d={value:k,computed:j}}});return d.value};b.min=function(g,f,e){if(!f&&b.isArray(g)){return Math.min.apply(Math,g)}var d;b.each(g,function(k,h){var j=f?f.call(e,k,h):k;if(d==null||jg?1:0}),"value")};b.sortedIndex=function(j,h,f){f=f||function(k){return k};var d=0,g=j.length;while(d>1;f(j[e])=0})})};b.zip=function(){var d=b.toArray(arguments);var g=b.max(b.pluck(d,"length"));var f=new Array(g);for(var e=0;e=0;i--){if(e[i]===d){return i}}return -1};b.bind=function(f,e){if(!e){return f}var d=b.toArray(arguments).slice(2);return function(){var g=d.concat(b.toArray(arguments));return f.apply(e,g)}};b.bindAll=function(){var d=b.toArray(arguments);var e=d.pop();b.each(d,function(f){e[f]=b.bind(e[f],e)})};b.delay=function(e,f){var d=b.toArray(arguments).slice(2);return setTimeout(function(){return e.apply(e,d)},f)};b.defer=function(d){return b.delay.apply(b,[d,1].concat(b.toArray(arguments).slice(1)))};b.wrap=function(d,e){return function(){var f=[d].concat(b.toArray(arguments));return e.apply(e,f)}};b.compose=function(){var d=b.toArray(arguments);return function(){for(var e=d.length-1;e>=0;e--){arguments=[d[e].apply(this,arguments)]}return arguments[0]}};b.keys=function(d){return b.pluck(d,"key")};b.values=function(d){return b.pluck(d,"value")};b.extend=function(d,f){for(var e in f){d[e]=f[e]}return d};b.clone=function(d){return b.extend({},d)};b.isEqual=function(e,d){if(e===d){return true}var h=typeof(e),k=typeof(d);if(h!=k){return false}if(e==d){return true}if(e.isEqual){return e.isEqual(d)}if(h!=="object"){return false}var f=b.keys(e),j=b.keys(d);if(f.length!=j.length){return false}for(var g in e){if(!b.isEqual(e[g],d[g])){return false}}return true};b.isElement=function(d){return !!(d&&d.nodeType==1)};b.isArray=function(d){return Object.prototype.toString.call(d)=="[object Array]"};b.isFunction=function(d){return typeof d=="function"};b.isUndefined=function(d){return typeof d=="undefined"};b.noConflict=function(){a._=c;return this};b.uniqueId=function(d){var e=this._idCounter=(this._idCounter||0)+1;return d?d+e:e};b.template=function(f,e){var d=new Function("obj","var p=[],print=function(){p.push.apply(p,arguments);};with(obj){p.push('"+f.replace(/[\r\t\n]/g," ").split("<%").join("\t").replace(/((^|%>)[^\t]*)'/g,"$1\r").replace(/\t=(.*?)%>/g,"',$1,'").split("\t").join("');").split("%>").join("p.push('").split("\r").join("\\'")+"');}return p.join('');");return e?d(e):d};b.forEach=b.each;b.inject=b.reduce;b.filter=b.select;b.every=b.all;b.some=b.any;if(!b.isUndefined(exports)){exports=b}})();
\ No newline at end of file
diff --git a/underscore.js b/underscore.js
index 0091abd90..7b48ab101 100644
--- a/underscore.js
+++ b/underscore.js
@@ -5,17 +5,22 @@
// Oliver Steele's Functional, And John Resig's Micro-Templating.
// For all details and documentation:
// http://documentcloud.github.com/underscore/
-window.Underscore = {
+
+(function() {
- VERSION : '0.1.1',
+ var root = (typeof window != 'undefined') ? window : exports;
- PREVIOUS_UNDERSCORE : window._,
+ var previousUnderscore = root._;
+ var _ = root._ = {};
+
+ _.VERSION = '0.2.0';
+
/*------------------------ Collection Functions: ---------------------------*/
// The cornerstone, an each implementation.
// Handles objects implementing forEach, each, arrays, and raw objects.
- each : function(obj, iterator, context) {
+ _.each = function(obj, iterator, context) {
var index = 0;
try {
if (obj.forEach) {
@@ -37,30 +42,30 @@ window.Underscore = {
if (e != '__break__') throw e;
}
return obj;
- },
+ };
// Return the results of applying the iterator to each element. Use Javascript
// 1.6's version of map, if possible.
- map : function(obj, iterator, context) {
+ _.map = function(obj, iterator, context) {
if (obj && obj.map) return obj.map(iterator, context);
var results = [];
_.each(obj, function(value, index) {
results.push(iterator.call(context, value, index));
});
return results;
- },
+ };
- // Inject builds up a single result from a list of values. Also known as
- // reduce, or foldl.
- inject : function(obj, memo, iterator, context) {
+ // Reduce builds up a single result from a list of values. Also known as
+ // inject, or foldl.
+ _.reduce = function(obj, memo, iterator, context) {
_.each(obj, function(value, index) {
memo = iterator.call(context, memo, value, index);
});
return memo;
- },
+ };
// Return the first value which passes a truth test.
- detect : function(obj, iterator, context) {
+ _.detect = function(obj, iterator, context) {
var result;
_.each(obj, function(value, index) {
if (iterator.call(context, value, index)) {
@@ -69,31 +74,31 @@ window.Underscore = {
}
});
return result;
- },
+ };
// Return all the elements that pass a truth test. Use Javascript 1.6's
// filter(), if it exists.
- select : function(obj, iterator, context) {
+ _.select = function(obj, iterator, context) {
if (obj.filter) return obj.filter(iterator, context);
var results = [];
_.each(obj, function(value, index) {
if (iterator.call(context, value, index)) results.push(value);
});
return results;
- },
+ };
// Return all the elements for which a truth test fails.
- reject : function(obj, iterator, context) {
+ _.reject = function(obj, iterator, context) {
var results = [];
_.each(obj, function(value, index) {
if (!iterator.call(context, value, index)) results.push(value);
});
return results;
- },
+ };
// Determine whether all of the elements match a truth test. Delegate to
// Javascript 1.6's every(), if it is present.
- all : function(obj, iterator, context) {
+ _.all = function(obj, iterator, context) {
iterator = iterator || function(v){ return v; };
if (obj.every) return obj.every(iterator, context);
var result = true;
@@ -102,11 +107,11 @@ window.Underscore = {
if (!result) throw '__break__';
});
return result;
- },
+ };
// Determine if at least one element in the object matches a truth test. Use
// Javascript 1.6's some(), if it exists.
- any : function(obj, iterator, context) {
+ _.any = function(obj, iterator, context) {
iterator = iterator || function(v) { return v; };
if (obj.some) return obj.some(iterator, context);
var result = false;
@@ -114,11 +119,11 @@ window.Underscore = {
if (result = !!iterator.call(context, value, index)) throw '__break__';
});
return result;
- },
+ };
// Determine if a given value is included in the array or object,
// based on '==='.
- include : function(obj, target) {
+ _.include = function(obj, target) {
if (_.isArray(obj)) return _.indexOf(obj, target) != -1;
var found = false;
_.each(obj, function(pair) {
@@ -128,25 +133,25 @@ window.Underscore = {
}
});
return found;
- },
+ };
// Invoke a method with arguments on every item in a collection.
- invoke : function(obj, method) {
+ _.invoke = function(obj, method) {
var args = _.toArray(arguments).slice(2);
return _.map(obj, function(value) {
return (method ? value[method] : value).apply(value, args);
});
- },
+ };
// Optimized version of a common use case of map: fetching a property.
- pluck : function(obj, key) {
+ _.pluck = function(obj, key) {
var results = [];
_.each(obj, function(value){ results.push(value[key]); });
return results;
- },
+ };
// Return the maximum item or (item-based computation).
- max : function(obj, iterator, context) {
+ _.max = function(obj, iterator, context) {
if (!iterator && _.isArray(obj)) return Math.max.apply(Math, obj);
var result;
_.each(obj, function(value, index) {
@@ -154,10 +159,10 @@ window.Underscore = {
if (result == null || computed >= result.computed) result = {value : value, computed : computed};
});
return result.value;
- },
+ };
// Return the minimum element (or element-based computation).
- min : function(obj, iterator, context) {
+ _.min = function(obj, iterator, context) {
if (!iterator && _.isArray(obj)) return Math.min.apply(Math, obj);
var result;
_.each(obj, function(value, index) {
@@ -165,10 +170,10 @@ window.Underscore = {
if (result == null || computed < result.computed) result = {value : value, computed : computed};
});
return result.value;
- },
+ };
// Sort the object's values by a criteria produced by an iterator.
- sortBy : function(obj, iterator, context) {
+ _.sortBy = function(obj, iterator, context) {
return _.pluck(_.map(obj, function(value, index) {
return {
value : value,
@@ -178,11 +183,11 @@ window.Underscore = {
var a = left.criteria, b = right.criteria;
return a < b ? -1 : a > b ? 1 : 0;
}), 'value');
- },
+ };
// Use a comparator function to figure out at what index an object should
// be inserted so as to maintain order. Uses binary search.
- sortedIndex : function(array, obj, iterator) {
+ _.sortedIndex = function(array, obj, iterator) {
iterator = iterator || function(val) { return val; };
var low = 0, high = array.length;
while (low < high) {
@@ -190,163 +195,182 @@ window.Underscore = {
iterator(array[mid]) < iterator(obj) ? low = mid + 1 : high = mid;
}
return low;
- },
+ };
// Convert anything iterable into a real, live array.
- toArray : function(iterable) {
+ _.toArray = function(iterable) {
if (!iterable) return [];
if (_.isArray(iterable)) return iterable;
return _.map(iterable, function(val){ return val; });
- },
+ };
// Return the number of elements in an object.
- size : function(obj) {
+ _.size = function(obj) {
return _.toArray(obj).length;
- },
+ };
/*-------------------------- Array Functions: ------------------------------*/
// Get the first element of an array.
- first : function(array) {
+ _.first = function(array) {
return array[0];
- },
+ };
// Get the last element of an array.
- last : function(array) {
+ _.last = function(array) {
return array[array.length - 1];
- },
+ };
// Trim out all falsy values from an array.
- compact : function(array) {
+ _.compact = function(array) {
return _.select(array, function(value){ return !!value; });
- },
+ };
// Return a completely flattened version of an array.
- flatten : function(array) {
- return _.inject(array, [], function(memo, value) {
+ _.flatten = function(array) {
+ return _.reduce(array, [], function(memo, value) {
if (_.isArray(value)) return memo.concat(_.flatten(value));
memo.push(value);
return memo;
});
- },
+ };
// Return a version of the array that does not contain the specified value(s).
- without : function(array) {
+ _.without = function(array) {
var values = array.slice.call(arguments, 0);
return _.select(array, function(value){ return !_.include(values, value); });
- },
+ };
// Produce a duplicate-free version of the array. If the array has already
// been sorted, you have the option of using a faster algorithm.
- uniq : function(array, isSorted) {
- return _.inject(array, [], function(memo, el, i) {
+ _.uniq = function(array, isSorted) {
+ return _.reduce(array, [], function(memo, el, i) {
if (0 == i || (isSorted ? _.last(memo) != el : !_.include(memo, el))) memo.push(el);
return memo;
});
- },
+ };
// Produce an array that contains every item shared between all the
// passed-in arrays.
- intersect : function(array) {
+ _.intersect = function(array) {
var rest = _.toArray(arguments).slice(1);
return _.select(_.uniq(array), function(item) {
return _.all(rest, function(other) {
return _.indexOf(other, item) >= 0;
});
});
- },
+ };
// Zip together multiple lists into a single array -- elements that share
// an index go together.
- zip : function() {
+ _.zip = function() {
var args = _.toArray(arguments);
var length = _.max(_.pluck(args, 'length'));
var results = new Array(length);
for (var i=0; i=0; i--) if (array[i] === item) return i;
+ return -1;
+ };
/* ----------------------- Function Functions: -----------------------------*/
// Create a function bound to a given object (assigning 'this', and arguments,
// optionally). Binding with arguments is also known as 'curry'.
- bind : function(func, context) {
+ _.bind = function(func, context) {
if (!context) return func;
var args = _.toArray(arguments).slice(2);
return function() {
var a = args.concat(_.toArray(arguments));
return func.apply(context, a);
};
- },
+ };
// Bind all of an object's methods to that object. Useful for ensuring that
// all callbacks defined on an object belong to it.
- bindAll : function() {
+ _.bindAll = function() {
var args = _.toArray(arguments);
var context = args.pop();
_.each(args, function(methodName) {
context[methodName] = _.bind(context[methodName], context);
});
- },
+ };
// Delays a function for the given number of milliseconds, and then calls
// it with the arguments supplied.
- delay : function(func, wait) {
+ _.delay = function(func, wait) {
var args = _.toArray(arguments).slice(2);
- return window.setTimeout(function(){ return func.apply(func, args); }, wait);
- },
+ return setTimeout(function(){ return func.apply(func, args); }, wait);
+ };
// Defers a function, scheduling it to run after the current call stack has
// cleared.
- defer : function(func) {
+ _.defer = function(func) {
return _.delay.apply(_, [func, 1].concat(_.toArray(arguments).slice(1)));
- },
+ };
// Returns the first function passed as an argument to the second,
// allowing you to adjust arguments, run code before and after, and
// conditionally execute the original function.
- wrap : function(func, wrapper) {
+ _.wrap = function(func, wrapper) {
return function() {
var args = [func].concat(_.toArray(arguments));
return wrapper.apply(wrapper, args);
};
- },
+ };
+
+ // Returns a function that is the composition of a list of functions, each
+ // consuming the return value of the function that follows.
+ _.compose = function() {
+ var funcs = _.toArray(arguments);
+ return function() {
+ for (var i=funcs.length-1; i >= 0; i--) {
+ arguments = [funcs[i].apply(this, arguments)];
+ }
+ return arguments[0];
+ };
+ };
/* ------------------------- Object Functions: ---------------------------- */
// Retrieve the names of an object's properties.
- keys : function(obj) {
+ _.keys = function(obj) {
return _.pluck(obj, 'key');
- },
+ };
// Retrieve the values of an object's properties.
- values : function(obj) {
+ _.values = function(obj) {
return _.pluck(obj, 'value');
- },
+ };
// Extend a given object with all of the properties in a source object.
- extend : function(destination, source) {
+ _.extend = function(destination, source) {
for (var property in source) destination[property] = source[property];
return destination;
- },
+ };
// Create a (shallow-cloned) duplicate of an object.
- clone : function(obj) {
+ _.clone = function(obj) {
return _.extend({}, obj);
- },
+ };
// Perform a deep comparison to check if two objects are equal.
- isEqual : function(a, b) {
+ _.isEqual = function(a, b) {
// Check object identity.
if (a === b) return true;
// Different types?
@@ -365,47 +389,47 @@ window.Underscore = {
// Recursive comparison of contents.
for (var key in a) if (!_.isEqual(a[key], b[key])) return false;
return true;
- },
+ };
// Is a given value a DOM element?
- isElement : function(obj) {
+ _.isElement = function(obj) {
return !!(obj && obj.nodeType == 1);
- },
+ };
// Is a given value a real Array?
- isArray : function(obj) {
+ _.isArray = function(obj) {
return Object.prototype.toString.call(obj) == '[object Array]';
- },
+ };
// Is a given value a Function?
- isFunction : function(obj) {
+ _.isFunction = function(obj) {
return typeof obj == 'function';
- },
+ };
// Is a given variable undefined?
- isUndefined : function(obj) {
+ _.isUndefined = function(obj) {
return typeof obj == 'undefined';
- },
+ };
/* -------------------------- Utility Functions: -------------------------- */
// Run Underscore.js in noConflict mode, returning the '_' variable to its
// previous owner. Returns a reference to the Underscore object.
- noConflict : function() {
- window._ = Underscore.PREVIOUS_UNDERSCORE;
+ _.noConflict = function() {
+ root._ = previousUnderscore;
return this;
- },
+ };
// Generate a unique integer id (unique within the entire client session).
// Useful for temporary DOM ids.
- uniqueId : function(prefix) {
+ _.uniqueId = function(prefix) {
var id = this._idCounter = (this._idCounter || 0) + 1;
return prefix ? prefix + id : id;
- },
+ };
// Javascript templating a-la ERB, pilfered from John Resig's
// "Secrets of the Javascript Ninja", page 83.
- template : function(str, data) {
+ _.template = function(str, data) {
var fn = new Function('obj',
'var p=[],print=function(){p.push.apply(p,arguments);};' +
'with(obj){p.push(\'' +
@@ -419,8 +443,18 @@ window.Underscore = {
.split("\r").join("\\'")
+ "');}return p.join('');");
return data ? fn(data) : fn;
- }
+ };
-};
+ /*------------------------------- Aliases ----------------------------------*/
-window._ = Underscore;
+ _.forEach = _.each;
+ _.inject = _.reduce;
+ _.filter = _.select;
+ _.every = _.all;
+ _.some = _.any;
+
+ /*------------------------- Export for ServerJS ----------------------------*/
+
+ if (!_.isUndefined(exports)) exports = _;
+
+})();