version 0.2.0 is out, with inject -> reduce, JS standard methodname aliases, a compose(), and a lastIndexOf()

This commit is contained in:
Jeremy Ashkenas
2009-10-28 18:49:50 -04:00
parent 6d52832a73
commit 4a83fcdd26
8 changed files with 294 additions and 158 deletions

View File

@@ -33,9 +33,14 @@
h1, h2, h3, h4, h5, h6 { h1, h2, h3, h4, h5, h6 {
margin-top: 40px; margin-top: 40px;
} }
b.method_name { b.header {
font-size: 18px; font-size: 18px;
} }
span.alias {
font-size: 14px;
font-style: italic;
margin-left: 20px;
}
table, tr, td { table, tr, td {
margin: 0; padding: 0; margin: 0; padding: 0;
} }
@@ -76,7 +81,7 @@
</p> </p>
<p> <p>
Underscore provides 42-odd functions that support both the usual Underscore provides 44-odd functions that support both the usual
functional suspects: <b>map</b>, <b>select</b>, <b>invoke</b> &mdash; functional suspects: <b>map</b>, <b>select</b>, <b>invoke</b> &mdash;
as well as more specialized helpers: function binding, javascript as well as more specialized helpers: function binding, javascript
templating, deep equality testing, and so on. It delegates to built-in templating, deep equality testing, and so on. It delegates to built-in
@@ -102,11 +107,11 @@
<p> <p>
<table> <table>
<tr> <tr>
<td><a href="underscore.js">Development Version (0.1.1)</a></td> <td><a href="underscore.js">Development Version (0.2.0)</a></td>
<td><i>16kb, Uncompressed with Comments</i></td> <td><i>16kb, Uncompressed with Comments</i></td>
</tr> </tr>
<tr> <tr>
<td><a href="underscore-min.js">Production Version (0.1.1)</a></td> <td><a href="underscore-min.js">Production Version (0.2.0)</a></td>
<td><i>4kb, Packed and Gzipped</i></td> <td><i>4kb, Packed and Gzipped</i></td>
</tr> </tr>
</table> </table>
@@ -118,7 +123,7 @@
<b>Collections</b> <b>Collections</b>
<br /> <br />
<span class="methods"><a href="#each">each</a>, <a href="#map">map</a>, <span class="methods"><a href="#each">each</a>, <a href="#map">map</a>,
<a href="#inject">inject</a>, <a href="#detect">detect</a>, <a href="#select">select</a>, <a href="#reject">reject</a>, <a href="#all">all</a>, <a href="#reduce">reduce</a>, <a href="#detect">detect</a>, <a href="#select">select</a>, <a href="#reject">reject</a>, <a href="#all">all</a>,
<a href="#any">any</a>, <a href="#include">include</a>, <a href="#invoke">invoke</a>, <a href="#pluck">pluck</a>, <a href="#max">max</a>, <a href="#any">any</a>, <a href="#include">include</a>, <a href="#invoke">invoke</a>, <a href="#pluck">pluck</a>, <a href="#max">max</a>,
<a href="#min">min</a>, <a href="#sortBy">sortBy</a>, <a href="#sortedIndex">sortedIndex</a>, <a href="#toArray">toArray</a>, <a href="#min">min</a>, <a href="#sortBy">sortBy</a>, <a href="#sortedIndex">sortedIndex</a>, <a href="#toArray">toArray</a>,
<a href="#size">size</a></span> <a href="#size">size</a></span>
@@ -129,14 +134,15 @@
<br /> <br />
<span class="methods"><a href="#first">first</a>, <a href="#last">last</a>, <span class="methods"><a href="#first">first</a>, <a href="#last">last</a>,
<a href="#compact">compact</a>, <a href="#flatten">flatten</a>, <a href="#without">without</a>, <a href="#uniq">uniq</a>, <a href="#compact">compact</a>, <a href="#flatten">flatten</a>, <a href="#without">without</a>, <a href="#uniq">uniq</a>,
<a href="#intersect">intersect</a>, <a href="#zip">zip</a>, <a href="#indexOf">indexOf</a></span> <a href="#intersect">intersect</a>, <a href="#zip">zip</a>, <a href="#indexOf">indexOf</a></span>,
<a href="#lastIndexOf">lastIndexOf</a></span>
</p> </p>
<p> <p>
<b>Functions</b> <b>Functions</b>
<br /> <br />
<span class="methods"><a href="#bind">bind</a>, <a href="#bindAll">bindAll</a>, <a href="#delay">delay</a>, <span class="methods"><a href="#bind">bind</a>, <a href="#bindAll">bindAll</a>, <a href="#delay">delay</a>,
<a href="#defer">defer</a>, <a href="#wrap">wrap</a></span> <a href="#defer">defer</a>, <a href="#wrap">wrap</a></span>, <a href="#compose">compose</a></span>
</p> </p>
<p> <p>
@@ -160,7 +166,8 @@
<h2>Collection Functions (Arrays or Objects)</h2> <h2>Collection Functions (Arrays or Objects)</h2>
<p id="each"> <p id="each">
<b class="method_name">each</b><code>_.each(list, iterator, [context])</code> <b class="header">each</b><code>_.each(list, iterator, [context])</code>
<span class="alias">Alias: <b>forEach</b></span>
<br /> <br />
Iterates over a <b>list</b> of elements, yielding each in turn to an <b>iterator</b> Iterates over a <b>list</b> of elements, yielding each in turn to an <b>iterator</b>
function. The <b>iterator</b> is bound to the <b>context</b> object, if one is function. The <b>iterator</b> is bound to the <b>context</b> object, if one is
@@ -174,7 +181,7 @@ _.each([1, 2, 3], function(num){ alert(num); });
=&gt; alerts each number in turn...</pre> =&gt; alerts each number in turn...</pre>
<p id="map"> <p id="map">
<b class="method_name">map</b><code>_.map(list, iterator, [context])</code> <b class="header">map</b><code>_.map(list, iterator, [context])</code>
<br /> <br />
Produces a new array of values by mapping each value in <b>list</b> Produces a new array of values by mapping each value in <b>list</b>
through a transformation function (<b>iterator</b>). If the native through a transformation function (<b>iterator</b>). If the native
@@ -184,21 +191,22 @@ _.each([1, 2, 3], function(num){ alert(num); });
_.map([1, 2, 3], function(num){ return num * 3 }); _.map([1, 2, 3], function(num){ return num * 3 });
=&gt; [3, 6, 9]</pre> =&gt; [3, 6, 9]</pre>
<p id="inject"> <p id="reduce">
<b class="method_name">inject</b><code>_.inject(list, memo, iterator, [context])</code> <b class="header">reduce</b><code>_.reduce(list, memo, iterator, [context])</code>
<span class="alias">Alias: <b>inject</b></span>
<br /> <br />
Also known as <b>reduce</b> and <b>foldl</b>, <b>inject</b> reduces a Also known as <b>inject</b> and <b>foldl</b>, <b>reduce</b> boils down a
<b>list</b> of values into a single value. <b>Memo</b> is the initial state <b>list</b> of values into a single value. <b>Memo</b> is the initial state
of the reduction, and each successive step of it should be returned by of the reduction, and each successive step of it should be returned by
<b>iterator</b>. <b>iterator</b>.
</p> </p>
<pre> <pre>
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 });
=&gt; 6 =&gt; 6
</pre> </pre>
<p id="detect"> <p id="detect">
<b class="method_name">detect</b><code>_.detect(list, iterator, [context])</code> <b class="header">detect</b><code>_.detect(list, iterator, [context])</code>
<br /> <br />
Looks through each value in the <b>list</b>, returning the first one that Looks through each value in the <b>list</b>, returning the first one that
passes a truth test (<b>iterator</b>). The function returns as passes a truth test (<b>iterator</b>). The function returns as
@@ -211,7 +219,8 @@ var even = _.detect([1, 2, 3, 4, 5, 6], function(num){ return num % 2 == 0; });
</pre> </pre>
<p id="select"> <p id="select">
<b class="method_name">select</b><code>_.select(list, iterator, [context])</code> <b class="header">select</b><code>_.select(list, iterator, [context])</code>
<span class="alias">Alias: <b>filter</b></span>
<br /> <br />
Looks through each value in the <b>list</b>, returning an array of all Looks through each value in the <b>list</b>, returning an array of all
the values that pass a truth test (<b>iterator</b>). Delegates to the the values that pass a truth test (<b>iterator</b>). Delegates to the
@@ -223,7 +232,7 @@ var evens = _.select([1, 2, 3, 4, 5, 6], function(num){ return num % 2 == 0; });
</pre> </pre>
<p id="reject"> <p id="reject">
<b class="method_name">reject</b><code>_.reject(list, iterator, [context])</code> <b class="header">reject</b><code>_.reject(list, iterator, [context])</code>
<br /> <br />
Returns the values in <b>list</b> without the elements that the truth Returns the values in <b>list</b> without the elements that the truth
test (<b>iterator</b>) passes. The opposite of <b>select</b>. test (<b>iterator</b>) passes. The opposite of <b>select</b>.
@@ -234,7 +243,8 @@ var odds = _.reject([1, 2, 3, 4, 5, 6], function(num){ return num % 2 == 0; });
</pre> </pre>
<p id="all"> <p id="all">
<b class="method_name">all</b><code>_.all(list, [iterator], [context])</code> <b class="header">all</b><code>_.all(list, [iterator], [context])</code>
<span class="alias">Alias: <b>every</b></span>
<br /> <br />
Returns <i>true</i> if all of the values in the <b>list</b> pass the <b>iterator</b> Returns <i>true</i> if all of the values in the <b>list</b> pass the <b>iterator</b>
truth test. If an <b>iterator</b> is not provided, the truthy value of truth test. If an <b>iterator</b> is not provided, the truthy value of
@@ -247,7 +257,8 @@ _.all([true, 1, null, 'yes']);
</pre> </pre>
<p id="any"> <p id="any">
<b class="method_name">any</b><code>_.any(list, [iterator], [context])</code> <b class="header">any</b><code>_.any(list, [iterator], [context])</code>
<span class="alias">Alias: <b>some</b></span>
<br /> <br />
Returns <i>true</i> if any of the values in the <b>list</b> pass the Returns <i>true</i> if any of the values in the <b>list</b> pass the
<b>iterator</b> truth test. Short-circuits and stops traversing the list <b>iterator</b> truth test. Short-circuits and stops traversing the list
@@ -260,7 +271,7 @@ _.any([null, 0, 'yes', false]);
</pre> </pre>
<p id="include"> <p id="include">
<b class="method_name">include</b><code>_.include(list, value)</code> <b class="header">include</b><code>_.include(list, value)</code>
<br /> <br />
Returns <i>true</i> if the <b>value</b> is present in the <b>list</b>, using Returns <i>true</i> if the <b>value</b> is present in the <b>list</b>, using
<i>===</i> to test equality. Uses <b>indexOf</b> internally, if <b>list</b> <i>===</i> to test equality. Uses <b>indexOf</b> internally, if <b>list</b>
@@ -272,7 +283,7 @@ _.include([1, 2, 3], 3);
</pre> </pre>
<p id="invoke"> <p id="invoke">
<b class="method_name">invoke</b><code>_.invoke(list, methodName, [*arguments])</code> <b class="header">invoke</b><code>_.invoke(list, methodName, [*arguments])</code>
<br /> <br />
Calls the method named by <b>methodName</b> on each value in the <b>list</b>. Calls the method named by <b>methodName</b> on each value in the <b>list</b>.
Any extra arguments passed to <b>invoke</b> will be forwarded on to the Any extra arguments passed to <b>invoke</b> will be forwarded on to the
@@ -284,7 +295,7 @@ _.invoke([[5, 1, 7], [3, 2, 1]], 'sort');
</pre> </pre>
<p id="pluck"> <p id="pluck">
<b class="method_name">pluck</b><code>_.pluck(list, propertyName)</code> <b class="header">pluck</b><code>_.pluck(list, propertyName)</code>
<br /> <br />
An optimized version of what is perhaps the most common use-case for An optimized version of what is perhaps the most common use-case for
<b>map</b>: returning a list of property values. <b>map</b>: returning a list of property values.
@@ -296,7 +307,7 @@ _.pluck(stooges, 'name');
</pre> </pre>
<p id="max"> <p id="max">
<b class="method_name">max</b><code>_.max(list, [iterator], [context])</code> <b class="header">max</b><code>_.max(list, [iterator], [context])</code>
<br /> <br />
Returns the maximum value in <b>list</b>. If <b>iterator</b> is passed, Returns the maximum value in <b>list</b>. If <b>iterator</b> is passed,
it will be used on each value to generate the criterion by which the 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; });
</pre> </pre>
<p id="min"> <p id="min">
<b class="method_name">min</b><code>_.min(list, [iterator], [context])</code> <b class="header">min</b><code>_.min(list, [iterator], [context])</code>
<br /> <br />
Returns the minimum value in <b>list</b>. If <b>iterator</b> is passed, Returns the minimum value in <b>list</b>. If <b>iterator</b> is passed,
it will be used on each value to generate the criterion by which the it will be used on each value to generate the criterion by which the
@@ -322,7 +333,7 @@ _.min(numbers);
</pre> </pre>
<p id="sortBy"> <p id="sortBy">
<b class="method_name">sortBy</b><code>_.sortBy(list, iterator, [context])</code> <b class="header">sortBy</b><code>_.sortBy(list, iterator, [context])</code>
<br /> <br />
Returns a sorted <b>list</b>, ranked by the results of running each Returns a sorted <b>list</b>, ranked by the results of running each
value through <b>iterator</b>. value through <b>iterator</b>.
@@ -333,7 +344,7 @@ _.sortBy([1, 2, 3, 4, 5, 6], function(num){ return Math.sin(num); });
</pre> </pre>
<p id="sortedIndex"> <p id="sortedIndex">
<b class="method_name">sortedIndex</b><code>_.sortedIndex(list, value, [iterator])</code> <b class="header">sortedIndex</b><code>_.sortedIndex(list, value, [iterator])</code>
<br /> <br />
Uses a binary search to determine the index at which the <b>value</b> Uses a binary search to determine the index at which the <b>value</b>
should be inserted into the <b>list</b> in order to maintain the <b>list</b>'s should be inserted into the <b>list</b> in order to maintain the <b>list</b>'s
@@ -346,7 +357,7 @@ _.sortedIndex([10, 20, 30, 40, 50], 35);
</pre> </pre>
<p id="toArray"> <p id="toArray">
<b class="method_name">toArray</b><code>_.toArray(list)</code> <b class="header">toArray</b><code>_.toArray(list)</code>
<br /> <br />
Converts the <b>list</b> (anything that can be iterated over), into a Converts the <b>list</b> (anything that can be iterated over), into a
real Array. Useful for transmuting the <b>arguments</b> object. real Array. Useful for transmuting the <b>arguments</b> object.
@@ -357,7 +368,7 @@ _.sortedIndex([10, 20, 30, 40, 50], 35);
</pre> </pre>
<p id="size"> <p id="size">
<b class="method_name">size</b><code>_.size(list)</code> <b class="header">size</b><code>_.size(list)</code>
<br /> <br />
Return the number of values in the <b>list</b>. Return the number of values in the <b>list</b>.
</p> </p>
@@ -369,7 +380,7 @@ _.size({one : 1, two : 2, three : 3});
<h2>Array Functions</h2> <h2>Array Functions</h2>
<p id="first"> <p id="first">
<b class="method_name">first</b><code>_.first(array)</code> <b class="header">first</b><code>_.first(array)</code>
<br /> <br />
Convenience to return the first element of an <b>array</b> (identical to <tt>array[0]</tt>). Convenience to return the first element of an <b>array</b> (identical to <tt>array[0]</tt>).
</p> </p>
@@ -379,7 +390,7 @@ _.first([3, 2, 1]);
</pre> </pre>
<p id="last"> <p id="last">
<b class="method_name">last</b><code>_.last(array)</code> <b class="header">last</b><code>_.last(array)</code>
<br /> <br />
Returns the last element of an <b>array</b>. Returns the last element of an <b>array</b>.
</p> </p>
@@ -389,7 +400,7 @@ _.last([3, 2, 1]);
</pre> </pre>
<p id="compact"> <p id="compact">
<b class="method_name">compact</b><code>_.compact(array)</code> <b class="header">compact</b><code>_.compact(array)</code>
<br /> <br />
Returns a copy of the <b>array</b> with all falsy values removed. Returns a copy of the <b>array</b> with all falsy values removed.
In Javascript, <i>false</i>, <i>null</i>, <i>0</i>, <i>""</i>, In Javascript, <i>false</i>, <i>null</i>, <i>0</i>, <i>""</i>,
@@ -401,7 +412,7 @@ _.compact([0, 1, false, 2, '', 3]);
</pre> </pre>
<p id="flatten"> <p id="flatten">
<b class="method_name">flatten</b><code>_.flatten(array)</code> <b class="header">flatten</b><code>_.flatten(array)</code>
<br /> <br />
Flattens a nested <b>array</b> (the nesting can be to any depth). Flattens a nested <b>array</b> (the nesting can be to any depth).
</p> </p>
@@ -411,7 +422,7 @@ _.flatten([1, [2], [3, [[[4]]]]]);
</pre> </pre>
<p id="without"> <p id="without">
<b class="method_name">without</b><code>_.without(array, [*values])</code> <b class="header">without</b><code>_.without(array, [*values])</code>
<br /> <br />
Returns a copy of the <b>array</b> with all instances of the <b>values</b> Returns a copy of the <b>array</b> with all instances of the <b>values</b>
removed. <i>===</i> is used for the equality test. removed. <i>===</i> is used for the equality test.
@@ -422,7 +433,7 @@ _.without([1, 2, 1, 0, 3, 1, 4], 0, 1);
</pre> </pre>
<p id="uniq"> <p id="uniq">
<b class="method_name">uniq</b><code>_.uniq(array, [isSorted])</code> <b class="header">uniq</b><code>_.uniq(array, [isSorted])</code>
<br /> <br />
Produces a duplicate-free version of the <b>array</b>, using <i>===</i> to test Produces a duplicate-free version of the <b>array</b>, using <i>===</i> to test
object equality. If you know in advance that the <b>array</b> is sorted, object equality. If you know in advance that the <b>array</b> is sorted,
@@ -434,7 +445,7 @@ _.uniq([1, 2, 1, 3, 1, 4]);
</pre> </pre>
<p id="intersect"> <p id="intersect">
<b class="method_name">intersect</b><code>_.intersect(*arrays)</code> <b class="header">intersect</b><code>_.intersect(*arrays)</code>
<br /> <br />
Computes the list of values that are the intersection of all the <b>arrays</b>. Computes the list of values that are the intersection of all the <b>arrays</b>.
Each value in the result is present in each of the <b>arrays</b>. Each value in the result is present in each of the <b>arrays</b>.
@@ -445,7 +456,7 @@ _.intersect([1, 2, 3], [101, 2, 1, 10], [2, 1]);
</pre> </pre>
<p id="zip"> <p id="zip">
<b class="method_name">zip</b><code>_.zip(*arrays)</code> <b class="header">zip</b><code>_.zip(*arrays)</code>
<br /> <br />
Merges together the values of each of the <b>arrays</b> with the Merges together the values of each of the <b>arrays</b> with the
values at the corresponding position. Useful when you have separate values at the corresponding position. Useful when you have separate
@@ -457,7 +468,7 @@ _.zip(['moe', 'larry', 'curly'], [30, 40, 50], [true, false, false]);
</pre> </pre>
<p id="indexOf"> <p id="indexOf">
<b class="method_name">indexOf</b><code>_.indexOf(array, value)</code> <b class="header">indexOf</b><code>_.indexOf(array, value)</code>
<br /> <br />
Returns the index at which <b>value</b> can be found in the <b>array</b>, Returns the index at which <b>value</b> can be found in the <b>array</b>,
or <i>-1</i> if value is not present in the <b>array</b>. Uses the native or <i>-1</i> if value is not present in the <b>array</b>. Uses the native
@@ -466,12 +477,24 @@ _.zip(['moe', 'larry', 'curly'], [30, 40, 50], [true, false, false]);
<pre> <pre>
_.indexOf([1, 2, 3], 2); _.indexOf([1, 2, 3], 2);
=&gt; 1 =&gt; 1
</pre>
<p id="lastIndexOf">
<b class="header">lastIndexOf</b><code>_.lastIndexOf(array, value)</code>
<br />
Returns the index of the last occurrence of <b>value</b> in the <b>array</b>,
or <i>-1</i> if value is not present. Uses the native <b>lastIndexOf</b>
function if possible.
</p>
<pre>
_.lastIndexOf([1, 2, 3, 1, 2, 3], 2);
=&gt; 4
</pre> </pre>
<h2>Function (uh, ahem) Functions</h2> <h2>Function (uh, ahem) Functions</h2>
<p id="bind"> <p id="bind">
<b class="method_name">bind</b><code>_.bind(function, context, [*arguments])</code> <b class="header">bind</b><code>_.bind(function, context, [*arguments])</code>
<br /> <br />
Bind a <b>function</b> to a <b>context</b> object, meaning that whenever Bind a <b>function</b> to a <b>context</b> object, meaning that whenever
the function is called, the value of <i>this</i> will be the <b>context</b>. the function is called, the value of <i>this</i> will be the <b>context</b>.
@@ -486,7 +509,7 @@ func();
</pre> </pre>
<p id="bindAll"> <p id="bindAll">
<b class="method_name">bindAll</b><code>_.bindAll(*methodNames, context)</code> <b class="header">bindAll</b><code>_.bindAll(*methodNames, context)</code>
<br /> <br />
Binds a number of methods on the <b>context</b> object, specified by Binds a number of methods on the <b>context</b> object, specified by
<b>methodNames</b>, to be run in the context of that object whenever they <b>methodNames</b>, to be run in the context of that object whenever they
@@ -506,7 +529,7 @@ jQuery('#underscore_button').bind('click', buttonView.onClick);
</pre> </pre>
<p id="delay"> <p id="delay">
<b class="method_name">delay</b><code>_.delay(function, wait, [*arguments])</code> <b class="header">delay</b><code>_.delay(function, wait, [*arguments])</code>
<br /> <br />
Much like <b>setTimeout</b>, invokes <b>function</b> after <b>wait</b> Much like <b>setTimeout</b>, invokes <b>function</b> after <b>wait</b>
milliseconds. If you pass the optional <b>arguments</b>, they will be milliseconds. If you pass the optional <b>arguments</b>, they will be
@@ -519,7 +542,7 @@ _.delay(log, 1000, 'logged later');
</pre> </pre>
<p id="defer"> <p id="defer">
<b class="method_name">defer</b><code>_.defer(function)</code> <b class="header">defer</b><code>_.defer(function)</code>
<br /> <br />
Defers invoking the <b>function</b> until the current call stack has cleared, Defers invoking the <b>function</b> until the current call stack has cleared,
similar to using <b>setTimeout</b> with a delay of 0. Useful for performing similar to using <b>setTimeout</b> with a delay of 0. Useful for performing
@@ -532,7 +555,7 @@ _.defer(function(){ alert('deferred'); });
</pre> </pre>
<p id="wrap"> <p id="wrap">
<b class="method_name">wrap</b><code>_.wrap(function, wrapper)</code> <b class="header">wrap</b><code>_.wrap(function, wrapper)</code>
<br /> <br />
Wraps the first <b>function</b> inside of the <b>wrapper</b> function, Wraps the first <b>function</b> inside of the <b>wrapper</b> function,
passing it as the first argument. This allows the <b>wrapper</b> to passing it as the first argument. This allows the <b>wrapper</b> to
@@ -545,13 +568,29 @@ hello = _.wrap(hello, function(func) {
return "before, " + func("moe") + ", after"; return "before, " + func("moe") + ", after";
}); });
hello(); hello();
=&gt; before, hello: moe, after =&gt; 'before, hello: moe, after'
</pre>
<p id="compose">
<b class="header">compose</b><code>_.compose(*functions)</code>
<br />
Returns the composition of a list of <b>functions</b>, where each function
consumes the return value of the function that follows. In math terms,
composing the functions <i>f()</i>, <i>g()</i>, and <i>h()</i> produces
<i>f(g(h()))</i>.
</p>
<pre>
var greet = function(name){ return "hi: " + name; };
var exclaim = function(statement){ return statement + "!"; };
var welcome = _.compose(greet, exclaim);
welcome('moe');
=&gt; 'hi: moe!'
</pre> </pre>
<h2>Object Functions</h2> <h2>Object Functions</h2>
<p id="keys"> <p id="keys">
<b class="method_name">keys</b><code>_.keys(object)</code> <b class="header">keys</b><code>_.keys(object)</code>
<br /> <br />
Retrieve all the names of the <b>object</b>'s properties. Retrieve all the names of the <b>object</b>'s properties.
</p> </p>
@@ -561,7 +600,7 @@ _.keys({one : 1, two : 2, three : 3});
</pre> </pre>
<p id="values"> <p id="values">
<b class="method_name">values</b><code>_.values(object)</code> <b class="header">values</b><code>_.values(object)</code>
<br /> <br />
Return all of the values of the <b>object</b>'s properties. Return all of the values of the <b>object</b>'s properties.
</p> </p>
@@ -571,7 +610,7 @@ _.values({one : 1, two : 2, three : 3});
</pre> </pre>
<p id="extend"> <p id="extend">
<b class="method_name">extend</b><code>_.extend(destination, source)</code> <b class="header">extend</b><code>_.extend(destination, source)</code>
<br /> <br />
Copy all of the properties in the <b>source</b> object over to the Copy all of the properties in the <b>source</b> object over to the
<b>destination</b> object. <b>destination</b> object.
@@ -582,7 +621,7 @@ _.extend({name : 'moe'}, {age : 50});
</pre> </pre>
<p id="clone"> <p id="clone">
<b class="method_name">clone</b><code>_.clone(object)</code> <b class="header">clone</b><code>_.clone(object)</code>
<br /> <br />
Create a shallow-copied clone of the <b>object</b>. Any nested objects Create a shallow-copied clone of the <b>object</b>. Any nested objects
or arrays will be copied by reference, not duplicated. or arrays will be copied by reference, not duplicated.
@@ -593,7 +632,7 @@ _.clone({name : 'moe'});
</pre> </pre>
<p id="isEqual"> <p id="isEqual">
<b class="method_name">isEqual</b><code>_.isEqual(object, other)</code> <b class="header">isEqual</b><code>_.isEqual(object, other)</code>
<br /> <br />
Performs an optimized deep comparison between the two objects, to determine Performs an optimized deep comparison between the two objects, to determine
if they should be considered equal. if they should be considered equal.
@@ -608,7 +647,7 @@ _.isEqual(moe, clone);
</pre> </pre>
<p id="isElement"> <p id="isElement">
<b class="method_name">isElement</b><code>_.isElement(object)</code> <b class="header">isElement</b><code>_.isElement(object)</code>
<br /> <br />
Returns <i>true</i> if <b>object</b> is a DOM element. Returns <i>true</i> if <b>object</b> is a DOM element.
</p> </p>
@@ -618,7 +657,7 @@ _.isElement(jQuery('body')[0]);
</pre> </pre>
<p id="isArray"> <p id="isArray">
<b class="method_name">isArray</b><code>_.isArray(object)</code> <b class="header">isArray</b><code>_.isArray(object)</code>
<br /> <br />
Returns <i>true</i> if <b>object</b> is an Array. Returns <i>true</i> if <b>object</b> is an Array.
</p> </p>
@@ -630,7 +669,7 @@ _.isArray([1,2,3]);
</pre> </pre>
<p id="isFunction"> <p id="isFunction">
<b class="method_name">isFunction</b><code>_.isFunction(object)</code> <b class="header">isFunction</b><code>_.isFunction(object)</code>
<br /> <br />
Returns <i>true</i> if <b>object</b> is a Function. Returns <i>true</i> if <b>object</b> is a Function.
</p> </p>
@@ -640,7 +679,7 @@ _.isFunction(alert);
</pre> </pre>
<p id="isUndefined"> <p id="isUndefined">
<b class="method_name">isUndefined</b><code>_.isUndefined(variable)</code> <b class="header">isUndefined</b><code>_.isUndefined(variable)</code>
<br /> <br />
Returns <i>true</i> if <b>variable</b> is <i>undefined</i>. Returns <i>true</i> if <b>variable</b> is <i>undefined</i>.
</p> </p>
@@ -652,7 +691,7 @@ _.isUndefined(window.missingVariable);
<h2>Utility Functions</h2> <h2>Utility Functions</h2>
<p id="noConflict"> <p id="noConflict">
<b class="method_name">noConflict</b><code>_.noConflict()</code> <b class="header">noConflict</b><code>_.noConflict()</code>
<br /> <br />
Give control of the "_" variable back to its previous owner. Returns Give control of the "_" variable back to its previous owner. Returns
a reference to the <b>Underscore</b> object. a reference to the <b>Underscore</b> object.
@@ -661,7 +700,7 @@ _.isUndefined(window.missingVariable);
var underscore = _.noConflict();</pre> var underscore = _.noConflict();</pre>
<p id="uniqueId"> <p id="uniqueId">
<b class="method_name">uniqueId</b><code>_.uniqueId([prefix])</code> <b class="header">uniqueId</b><code>_.uniqueId([prefix])</code>
<br /> <br />
Generate a globally-unique id for client-side models or DOM elements Generate a globally-unique id for client-side models or DOM elements
that need one. If <b>prefix</b> is passed, the id will be appended to it. that need one. If <b>prefix</b> is passed, the id will be appended to it.
@@ -672,7 +711,7 @@ _.uniqueId('contact_');
</pre> </pre>
<p id="template"> <p id="template">
<b class="method_name">template</b><code>_.template(templateString, [context])</code> <b class="header">template</b><code>_.template(templateString, [context])</code>
<br /> <br />
Compiles Javascript templates into functions that can be evaluated Compiles Javascript templates into functions that can be evaluated
for rendering. Useful for rendering complicated bits of HTML from JSON for rendering. Useful for rendering complicated bits of HTML from JSON
@@ -694,6 +733,26 @@ _.template(list, {people : ['moe', 'curly', 'larry']});
=&gt; "&lt;li&gt;moe&lt;/li&gt;&lt;li&gt;curly&lt;/li&gt;&lt;li&gt;larry&lt;/li&gt;" =&gt; "&lt;li&gt;moe&lt;/li&gt;&lt;li&gt;curly&lt;/li&gt;&lt;li&gt;larry&lt;/li&gt;"
</pre> </pre>
<h2>Change Log</h2>
<p>
<b class="header">0.2.0</b><br />
Added <tt>compose</tt> and <tt>lastIndexOf</tt>, renamed <tt>inject</tt> to
<tt>reduce</tt>, added aliases for <tt>inject</tt>, <tt>filter</tt>,
<tt>every</tt>, <tt>some</tt>, and <tt>forEach</tt>.
</p>
<p>
<b class="header">0.1.1</b><br />
Added <tt>noConflict</tt>, so that the "Underscore" object can be assigned to
other variables.
</p>
<p>
<b class="header">0.1.0</b><br />
Initial release of Underscore.js.
</p>
<p> <p>
<a href="http://documentcloud.org/" title="A DocumentCloud Project" style="background:none;"> <a href="http://documentcloud.org/" title="A DocumentCloud Project" style="background:none;">
<img src="http://jashkenas.s3.amazonaws.com/images/a_documentcloud_project.png" alt="A DocumentCloud Project" /> <img src="http://jashkenas.s3.amazonaws.com/images/a_documentcloud_project.png" alt="A DocumentCloud Project" />

12
package.json Normal file
View File

@@ -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 <jeremy@documentcloud.org>",
"maintainer": "Kris Kowal <kris.kowal@cixar.com>",
"contributors": [],
"dependencies": [],
"lib", "."
}

View File

@@ -49,4 +49,11 @@ $(document).ready(function() {
equals(_.indexOf(numbers, 2), 1, 'can compute indexOf, even without the native 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');
});
}); });

View File

@@ -14,6 +14,10 @@ $(document).ready(function() {
var answers = []; var answers = [];
_.each([1, 2, 3], function(num) { answers.push(num * this.multiplier);}, {multiplier : 5}); _.each([1, 2, 3], function(num) { answers.push(num * this.multiplier);}, {multiplier : 5});
equals(answers.join(', '), '5, 10, 15', 'context object property accessed'); 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() { test('collections: map', function() {
@@ -24,9 +28,12 @@ $(document).ready(function() {
equals(tripled.join(', '), '3, 6, 9', 'tripled numbers with context'); equals(tripled.join(', '), '3, 6, 9', 'tripled numbers with context');
}); });
test('collections: inject', function() { test('collections: reduce', function() {
var sum = _.inject([1,2,3], 0, function(sum, num){ return sum + num; }); var sum = _.reduce([1, 2, 3], 0, function(sum, num){ return sum + num; });
equals(sum, 6, 'can sum up an array'); 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() { test('collections: detect', function() {
@@ -35,12 +42,15 @@ $(document).ready(function() {
}); });
test('collections: select', 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'); 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() { 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'); 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([true, false, true]), 'one false value');
ok(_.all([0, 10, 28], function(num){ return num % 2 == 0; }), 'even numbers'); 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(!_.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() { test('collections: any', function() {
@@ -58,6 +69,7 @@ $(document).ready(function() {
ok(_.any([false, false, true]), 'one true value'); 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, 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(_.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() { test('collections: include', function() {

View File

@@ -48,4 +48,14 @@ $(document).ready(function() {
equals(backwards('moe'), 'hi: moe eom', 'wrapped the saluation 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');
});
}); });

View File

@@ -5,6 +5,8 @@ $(document).ready(function() {
test("utility: noConflict", function() { test("utility: noConflict", function() {
var underscore = _.noConflict(); var underscore = _.noConflict();
ok(underscore.isUndefined(_), "The '_' variable has been returned to its previous state."); 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; window._ = underscore;
}); });

2
underscore-min.js vendored

File diff suppressed because one or more lines are too long

View File

@@ -5,17 +5,22 @@
// Oliver Steele's Functional, And John Resig's Micro-Templating. // Oliver Steele's Functional, And John Resig's Micro-Templating.
// For all details and documentation: // For all details and documentation:
// http://documentcloud.github.com/underscore/ // http://documentcloud.github.com/underscore/
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: ---------------------------*/ /*------------------------ Collection Functions: ---------------------------*/
// The cornerstone, an each implementation. // The cornerstone, an each implementation.
// Handles objects implementing forEach, each, arrays, and raw objects. // Handles objects implementing forEach, each, arrays, and raw objects.
each : function(obj, iterator, context) { _.each = function(obj, iterator, context) {
var index = 0; var index = 0;
try { try {
if (obj.forEach) { if (obj.forEach) {
@@ -37,30 +42,30 @@ window.Underscore = {
if (e != '__break__') throw e; if (e != '__break__') throw e;
} }
return obj; return obj;
}, };
// Return the results of applying the iterator to each element. Use Javascript // Return the results of applying the iterator to each element. Use Javascript
// 1.6's version of map, if possible. // 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); if (obj && obj.map) return obj.map(iterator, context);
var results = []; var results = [];
_.each(obj, function(value, index) { _.each(obj, function(value, index) {
results.push(iterator.call(context, value, index)); results.push(iterator.call(context, value, index));
}); });
return results; return results;
}, };
// Inject builds up a single result from a list of values. Also known as // Reduce builds up a single result from a list of values. Also known as
// reduce, or foldl. // inject, or foldl.
inject : function(obj, memo, iterator, context) { _.reduce = function(obj, memo, iterator, context) {
_.each(obj, function(value, index) { _.each(obj, function(value, index) {
memo = iterator.call(context, memo, value, index); memo = iterator.call(context, memo, value, index);
}); });
return memo; return memo;
}, };
// Return the first value which passes a truth test. // Return the first value which passes a truth test.
detect : function(obj, iterator, context) { _.detect = function(obj, iterator, context) {
var result; var result;
_.each(obj, function(value, index) { _.each(obj, function(value, index) {
if (iterator.call(context, value, index)) { if (iterator.call(context, value, index)) {
@@ -69,31 +74,31 @@ window.Underscore = {
} }
}); });
return result; return result;
}, };
// Return all the elements that pass a truth test. Use Javascript 1.6's // Return all the elements that pass a truth test. Use Javascript 1.6's
// filter(), if it exists. // filter(), if it exists.
select : function(obj, iterator, context) { _.select = function(obj, iterator, context) {
if (obj.filter) return obj.filter(iterator, context); if (obj.filter) return obj.filter(iterator, context);
var results = []; var results = [];
_.each(obj, function(value, index) { _.each(obj, function(value, index) {
if (iterator.call(context, value, index)) results.push(value); if (iterator.call(context, value, index)) results.push(value);
}); });
return results; return results;
}, };
// Return all the elements for which a truth test fails. // Return all the elements for which a truth test fails.
reject : function(obj, iterator, context) { _.reject = function(obj, iterator, context) {
var results = []; var results = [];
_.each(obj, function(value, index) { _.each(obj, function(value, index) {
if (!iterator.call(context, value, index)) results.push(value); if (!iterator.call(context, value, index)) results.push(value);
}); });
return results; return results;
}, };
// Determine whether all of the elements match a truth test. Delegate to // Determine whether all of the elements match a truth test. Delegate to
// Javascript 1.6's every(), if it is present. // 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; }; iterator = iterator || function(v){ return v; };
if (obj.every) return obj.every(iterator, context); if (obj.every) return obj.every(iterator, context);
var result = true; var result = true;
@@ -102,11 +107,11 @@ window.Underscore = {
if (!result) throw '__break__'; if (!result) throw '__break__';
}); });
return result; return result;
}, };
// Determine if at least one element in the object matches a truth test. Use // Determine if at least one element in the object matches a truth test. Use
// Javascript 1.6's some(), if it exists. // Javascript 1.6's some(), if it exists.
any : function(obj, iterator, context) { _.any = function(obj, iterator, context) {
iterator = iterator || function(v) { return v; }; iterator = iterator || function(v) { return v; };
if (obj.some) return obj.some(iterator, context); if (obj.some) return obj.some(iterator, context);
var result = false; var result = false;
@@ -114,11 +119,11 @@ window.Underscore = {
if (result = !!iterator.call(context, value, index)) throw '__break__'; if (result = !!iterator.call(context, value, index)) throw '__break__';
}); });
return result; return result;
}, };
// Determine if a given value is included in the array or object, // Determine if a given value is included in the array or object,
// based on '==='. // based on '==='.
include : function(obj, target) { _.include = function(obj, target) {
if (_.isArray(obj)) return _.indexOf(obj, target) != -1; if (_.isArray(obj)) return _.indexOf(obj, target) != -1;
var found = false; var found = false;
_.each(obj, function(pair) { _.each(obj, function(pair) {
@@ -128,25 +133,25 @@ window.Underscore = {
} }
}); });
return found; return found;
}, };
// Invoke a method with arguments on every item in a collection. // Invoke a method with arguments on every item in a collection.
invoke : function(obj, method) { _.invoke = function(obj, method) {
var args = _.toArray(arguments).slice(2); var args = _.toArray(arguments).slice(2);
return _.map(obj, function(value) { return _.map(obj, function(value) {
return (method ? value[method] : value).apply(value, args); return (method ? value[method] : value).apply(value, args);
}); });
}, };
// Optimized version of a common use case of map: fetching a property. // Optimized version of a common use case of map: fetching a property.
pluck : function(obj, key) { _.pluck = function(obj, key) {
var results = []; var results = [];
_.each(obj, function(value){ results.push(value[key]); }); _.each(obj, function(value){ results.push(value[key]); });
return results; return results;
}, };
// Return the maximum item or (item-based computation). // 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); if (!iterator && _.isArray(obj)) return Math.max.apply(Math, obj);
var result; var result;
_.each(obj, function(value, index) { _.each(obj, function(value, index) {
@@ -154,10 +159,10 @@ window.Underscore = {
if (result == null || computed >= result.computed) result = {value : value, computed : computed}; if (result == null || computed >= result.computed) result = {value : value, computed : computed};
}); });
return result.value; return result.value;
}, };
// Return the minimum element (or element-based computation). // 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); if (!iterator && _.isArray(obj)) return Math.min.apply(Math, obj);
var result; var result;
_.each(obj, function(value, index) { _.each(obj, function(value, index) {
@@ -165,10 +170,10 @@ window.Underscore = {
if (result == null || computed < result.computed) result = {value : value, computed : computed}; if (result == null || computed < result.computed) result = {value : value, computed : computed};
}); });
return result.value; return result.value;
}, };
// Sort the object's values by a criteria produced by an iterator. // 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 _.pluck(_.map(obj, function(value, index) {
return { return {
value : value, value : value,
@@ -178,11 +183,11 @@ window.Underscore = {
var a = left.criteria, b = right.criteria; var a = left.criteria, b = right.criteria;
return a < b ? -1 : a > b ? 1 : 0; return a < b ? -1 : a > b ? 1 : 0;
}), 'value'); }), 'value');
}, };
// Use a comparator function to figure out at what index an object should // Use a comparator function to figure out at what index an object should
// be inserted so as to maintain order. Uses binary search. // 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; }; iterator = iterator || function(val) { return val; };
var low = 0, high = array.length; var low = 0, high = array.length;
while (low < high) { while (low < high) {
@@ -190,163 +195,182 @@ window.Underscore = {
iterator(array[mid]) < iterator(obj) ? low = mid + 1 : high = mid; iterator(array[mid]) < iterator(obj) ? low = mid + 1 : high = mid;
} }
return low; return low;
}, };
// Convert anything iterable into a real, live array. // Convert anything iterable into a real, live array.
toArray : function(iterable) { _.toArray = function(iterable) {
if (!iterable) return []; if (!iterable) return [];
if (_.isArray(iterable)) return iterable; if (_.isArray(iterable)) return iterable;
return _.map(iterable, function(val){ return val; }); return _.map(iterable, function(val){ return val; });
}, };
// Return the number of elements in an object. // Return the number of elements in an object.
size : function(obj) { _.size = function(obj) {
return _.toArray(obj).length; return _.toArray(obj).length;
}, };
/*-------------------------- Array Functions: ------------------------------*/ /*-------------------------- Array Functions: ------------------------------*/
// Get the first element of an array. // Get the first element of an array.
first : function(array) { _.first = function(array) {
return array[0]; return array[0];
}, };
// Get the last element of an array. // Get the last element of an array.
last : function(array) { _.last = function(array) {
return array[array.length - 1]; return array[array.length - 1];
}, };
// Trim out all falsy values from an array. // Trim out all falsy values from an array.
compact : function(array) { _.compact = function(array) {
return _.select(array, function(value){ return !!value; }); return _.select(array, function(value){ return !!value; });
}, };
// Return a completely flattened version of an array. // Return a completely flattened version of an array.
flatten : function(array) { _.flatten = function(array) {
return _.inject(array, [], function(memo, value) { return _.reduce(array, [], function(memo, value) {
if (_.isArray(value)) return memo.concat(_.flatten(value)); if (_.isArray(value)) return memo.concat(_.flatten(value));
memo.push(value); memo.push(value);
return memo; return memo;
}); });
}, };
// Return a version of the array that does not contain the specified value(s). // Return a version of the array that does not contain the specified value(s).
without : function(array) { _.without = function(array) {
var values = array.slice.call(arguments, 0); var values = array.slice.call(arguments, 0);
return _.select(array, function(value){ return !_.include(values, value); }); return _.select(array, function(value){ return !_.include(values, value); });
}, };
// Produce a duplicate-free version of the array. If the array has already // Produce a duplicate-free version of the array. If the array has already
// been sorted, you have the option of using a faster algorithm. // been sorted, you have the option of using a faster algorithm.
uniq : function(array, isSorted) { _.uniq = function(array, isSorted) {
return _.inject(array, [], function(memo, el, i) { return _.reduce(array, [], function(memo, el, i) {
if (0 == i || (isSorted ? _.last(memo) != el : !_.include(memo, el))) memo.push(el); if (0 == i || (isSorted ? _.last(memo) != el : !_.include(memo, el))) memo.push(el);
return memo; return memo;
}); });
}, };
// Produce an array that contains every item shared between all the // Produce an array that contains every item shared between all the
// passed-in arrays. // passed-in arrays.
intersect : function(array) { _.intersect = function(array) {
var rest = _.toArray(arguments).slice(1); var rest = _.toArray(arguments).slice(1);
return _.select(_.uniq(array), function(item) { return _.select(_.uniq(array), function(item) {
return _.all(rest, function(other) { return _.all(rest, function(other) {
return _.indexOf(other, item) >= 0; return _.indexOf(other, item) >= 0;
}); });
}); });
}, };
// Zip together multiple lists into a single array -- elements that share // Zip together multiple lists into a single array -- elements that share
// an index go together. // an index go together.
zip : function() { _.zip = function() {
var args = _.toArray(arguments); var args = _.toArray(arguments);
var length = _.max(_.pluck(args, 'length')); var length = _.max(_.pluck(args, 'length'));
var results = new Array(length); var results = new Array(length);
for (var i=0; i<length; i++) results[i] = _.pluck(args, String(i)); for (var i=0; i<length; i++) results[i] = _.pluck(args, String(i));
return results; return results;
}, };
// If the browser doesn't supply us with indexOf (I'm looking at you, MSIE), // If the browser doesn't supply us with indexOf (I'm looking at you, MSIE),
// we need this function. Return the position of the first occurence of an // we need this function. Return the position of the first occurence of an
// item in an array, or -1 if the item is not included in the array. // item in an array, or -1 if the item is not included in the array.
indexOf : function(array, item) { _.indexOf = function(array, item) {
if (array.indexOf) return array.indexOf(item); if (array.indexOf) return array.indexOf(item);
var length = array.length; for (i=0; i<array.length; i++) if (array[i] === item) return i;
for (i=0; i<length; i++) if (array[i] === item) return i;
return -1; return -1;
}, };
// Provide Javascript 1.6's lastIndexOf, delegating to the native function,
// if possible.
_.lastIndexOf = function(array, item) {
if (array.lastIndexOf) return array.lastIndexOf(item);
for (i=array.length - 1; i>=0; i--) if (array[i] === item) return i;
return -1;
};
/* ----------------------- Function Functions: -----------------------------*/ /* ----------------------- Function Functions: -----------------------------*/
// Create a function bound to a given object (assigning 'this', and arguments, // Create a function bound to a given object (assigning 'this', and arguments,
// optionally). Binding with arguments is also known as 'curry'. // optionally). Binding with arguments is also known as 'curry'.
bind : function(func, context) { _.bind = function(func, context) {
if (!context) return func; if (!context) return func;
var args = _.toArray(arguments).slice(2); var args = _.toArray(arguments).slice(2);
return function() { return function() {
var a = args.concat(_.toArray(arguments)); var a = args.concat(_.toArray(arguments));
return func.apply(context, a); return func.apply(context, a);
}; };
}, };
// Bind all of an object's methods to that object. Useful for ensuring that // Bind all of an object's methods to that object. Useful for ensuring that
// all callbacks defined on an object belong to it. // all callbacks defined on an object belong to it.
bindAll : function() { _.bindAll = function() {
var args = _.toArray(arguments); var args = _.toArray(arguments);
var context = args.pop(); var context = args.pop();
_.each(args, function(methodName) { _.each(args, function(methodName) {
context[methodName] = _.bind(context[methodName], context); context[methodName] = _.bind(context[methodName], context);
}); });
}, };
// Delays a function for the given number of milliseconds, and then calls // Delays a function for the given number of milliseconds, and then calls
// it with the arguments supplied. // it with the arguments supplied.
delay : function(func, wait) { _.delay = function(func, wait) {
var args = _.toArray(arguments).slice(2); 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 // Defers a function, scheduling it to run after the current call stack has
// cleared. // cleared.
defer : function(func) { _.defer = function(func) {
return _.delay.apply(_, [func, 1].concat(_.toArray(arguments).slice(1))); return _.delay.apply(_, [func, 1].concat(_.toArray(arguments).slice(1)));
}, };
// Returns the first function passed as an argument to the second, // Returns the first function passed as an argument to the second,
// allowing you to adjust arguments, run code before and after, and // allowing you to adjust arguments, run code before and after, and
// conditionally execute the original function. // conditionally execute the original function.
wrap : function(func, wrapper) { _.wrap = function(func, wrapper) {
return function() { return function() {
var args = [func].concat(_.toArray(arguments)); var args = [func].concat(_.toArray(arguments));
return wrapper.apply(wrapper, args); 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: ---------------------------- */ /* ------------------------- Object Functions: ---------------------------- */
// Retrieve the names of an object's properties. // Retrieve the names of an object's properties.
keys : function(obj) { _.keys = function(obj) {
return _.pluck(obj, 'key'); return _.pluck(obj, 'key');
}, };
// Retrieve the values of an object's properties. // Retrieve the values of an object's properties.
values : function(obj) { _.values = function(obj) {
return _.pluck(obj, 'value'); return _.pluck(obj, 'value');
}, };
// Extend a given object with all of the properties in a source object. // 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]; for (var property in source) destination[property] = source[property];
return destination; return destination;
}, };
// Create a (shallow-cloned) duplicate of an object. // Create a (shallow-cloned) duplicate of an object.
clone : function(obj) { _.clone = function(obj) {
return _.extend({}, obj); return _.extend({}, obj);
}, };
// Perform a deep comparison to check if two objects are equal. // Perform a deep comparison to check if two objects are equal.
isEqual : function(a, b) { _.isEqual = function(a, b) {
// Check object identity. // Check object identity.
if (a === b) return true; if (a === b) return true;
// Different types? // Different types?
@@ -365,47 +389,47 @@ window.Underscore = {
// Recursive comparison of contents. // Recursive comparison of contents.
for (var key in a) if (!_.isEqual(a[key], b[key])) return false; for (var key in a) if (!_.isEqual(a[key], b[key])) return false;
return true; return true;
}, };
// Is a given value a DOM element? // Is a given value a DOM element?
isElement : function(obj) { _.isElement = function(obj) {
return !!(obj && obj.nodeType == 1); return !!(obj && obj.nodeType == 1);
}, };
// Is a given value a real Array? // Is a given value a real Array?
isArray : function(obj) { _.isArray = function(obj) {
return Object.prototype.toString.call(obj) == '[object Array]'; return Object.prototype.toString.call(obj) == '[object Array]';
}, };
// Is a given value a Function? // Is a given value a Function?
isFunction : function(obj) { _.isFunction = function(obj) {
return typeof obj == 'function'; return typeof obj == 'function';
}, };
// Is a given variable undefined? // Is a given variable undefined?
isUndefined : function(obj) { _.isUndefined = function(obj) {
return typeof obj == 'undefined'; return typeof obj == 'undefined';
}, };
/* -------------------------- Utility Functions: -------------------------- */ /* -------------------------- Utility Functions: -------------------------- */
// Run Underscore.js in noConflict mode, returning the '_' variable to its // Run Underscore.js in noConflict mode, returning the '_' variable to its
// previous owner. Returns a reference to the Underscore object. // previous owner. Returns a reference to the Underscore object.
noConflict : function() { _.noConflict = function() {
window._ = Underscore.PREVIOUS_UNDERSCORE; root._ = previousUnderscore;
return this; return this;
}, };
// Generate a unique integer id (unique within the entire client session). // Generate a unique integer id (unique within the entire client session).
// Useful for temporary DOM ids. // Useful for temporary DOM ids.
uniqueId : function(prefix) { _.uniqueId = function(prefix) {
var id = this._idCounter = (this._idCounter || 0) + 1; var id = this._idCounter = (this._idCounter || 0) + 1;
return prefix ? prefix + id : id; return prefix ? prefix + id : id;
}, };
// Javascript templating a-la ERB, pilfered from John Resig's // Javascript templating a-la ERB, pilfered from John Resig's
// "Secrets of the Javascript Ninja", page 83. // "Secrets of the Javascript Ninja", page 83.
template : function(str, data) { _.template = function(str, data) {
var fn = new Function('obj', var fn = new Function('obj',
'var p=[],print=function(){p.push.apply(p,arguments);};' + 'var p=[],print=function(){p.push.apply(p,arguments);};' +
'with(obj){p.push(\'' + 'with(obj){p.push(\'' +
@@ -419,8 +443,18 @@ window.Underscore = {
.split("\r").join("\\'") .split("\r").join("\\'")
+ "');}return p.join('');"); + "');}return p.join('');");
return data ? fn(data) : fn; 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 = _;
})();