root / doc / build / html / _static / underscore.js @ 6de88ee1
History | View | Annotate | Download (27.4 kB)
1 | 6de88ee1 | Stauros Kroustouris | // Underscore.js 1.1.6
|
---|---|---|---|
2 | 6de88ee1 | Stauros Kroustouris | // (c) 2011 Jeremy Ashkenas, DocumentCloud Inc.
|
3 | 6de88ee1 | Stauros Kroustouris | // Underscore is freely distributable under the MIT license.
|
4 | 6de88ee1 | Stauros Kroustouris | // Portions of Underscore are inspired or borrowed from Prototype,
|
5 | 6de88ee1 | Stauros Kroustouris | // Oliver Steele's Functional, and John Resig's Micro-Templating.
|
6 | 6de88ee1 | Stauros Kroustouris | // For all details and documentation:
|
7 | 6de88ee1 | Stauros Kroustouris | // http://documentcloud.github.com/underscore
|
8 | 6de88ee1 | Stauros Kroustouris | |
9 | 6de88ee1 | Stauros Kroustouris | (function() {
|
10 | 6de88ee1 | Stauros Kroustouris | |
11 | 6de88ee1 | Stauros Kroustouris | // Baseline setup
|
12 | 6de88ee1 | Stauros Kroustouris | // --------------
|
13 | 6de88ee1 | Stauros Kroustouris | |
14 | 6de88ee1 | Stauros Kroustouris | // Establish the root object, `window` in the browser, or `global` on the server.
|
15 | 6de88ee1 | Stauros Kroustouris | var root = this; |
16 | 6de88ee1 | Stauros Kroustouris | |
17 | 6de88ee1 | Stauros Kroustouris | // Save the previous value of the `_` variable.
|
18 | 6de88ee1 | Stauros Kroustouris | var previousUnderscore = root._;
|
19 | 6de88ee1 | Stauros Kroustouris | |
20 | 6de88ee1 | Stauros Kroustouris | // Establish the object that gets returned to break out of a loop iteration.
|
21 | 6de88ee1 | Stauros Kroustouris | var breaker = {};
|
22 | 6de88ee1 | Stauros Kroustouris | |
23 | 6de88ee1 | Stauros Kroustouris | // Save bytes in the minified (but not gzipped) version:
|
24 | 6de88ee1 | Stauros Kroustouris | var ArrayProto = Array.prototype, ObjProto = Object.prototype, FuncProto = Function.prototype;
|
25 | 6de88ee1 | Stauros Kroustouris | |
26 | 6de88ee1 | Stauros Kroustouris | // Create quick reference variables for speed access to core prototypes.
|
27 | 6de88ee1 | Stauros Kroustouris | var slice = ArrayProto.slice,
|
28 | 6de88ee1 | Stauros Kroustouris | unshift = ArrayProto.unshift, |
29 | 6de88ee1 | Stauros Kroustouris | toString = ObjProto.toString, |
30 | 6de88ee1 | Stauros Kroustouris | hasOwnProperty = ObjProto.hasOwnProperty; |
31 | 6de88ee1 | Stauros Kroustouris | |
32 | 6de88ee1 | Stauros Kroustouris | // All **ECMAScript 5** native function implementations that we hope to use
|
33 | 6de88ee1 | Stauros Kroustouris | // are declared here.
|
34 | 6de88ee1 | Stauros Kroustouris | var
|
35 | 6de88ee1 | Stauros Kroustouris | nativeForEach = ArrayProto.forEach, |
36 | 6de88ee1 | Stauros Kroustouris | nativeMap = ArrayProto.map, |
37 | 6de88ee1 | Stauros Kroustouris | nativeReduce = ArrayProto.reduce, |
38 | 6de88ee1 | Stauros Kroustouris | nativeReduceRight = ArrayProto.reduceRight, |
39 | 6de88ee1 | Stauros Kroustouris | nativeFilter = ArrayProto.filter, |
40 | 6de88ee1 | Stauros Kroustouris | nativeEvery = ArrayProto.every, |
41 | 6de88ee1 | Stauros Kroustouris | nativeSome = ArrayProto.some, |
42 | 6de88ee1 | Stauros Kroustouris | nativeIndexOf = ArrayProto.indexOf, |
43 | 6de88ee1 | Stauros Kroustouris | nativeLastIndexOf = ArrayProto.lastIndexOf, |
44 | 6de88ee1 | Stauros Kroustouris | nativeIsArray = Array.isArray, |
45 | 6de88ee1 | Stauros Kroustouris | nativeKeys = Object.keys, |
46 | 6de88ee1 | Stauros Kroustouris | nativeBind = FuncProto.bind; |
47 | 6de88ee1 | Stauros Kroustouris | |
48 | 6de88ee1 | Stauros Kroustouris | // Create a safe reference to the Underscore object for use below.
|
49 | 6de88ee1 | Stauros Kroustouris | var _ = function(obj) { return new wrapper(obj); }; |
50 | 6de88ee1 | Stauros Kroustouris | |
51 | 6de88ee1 | Stauros Kroustouris | // Export the Underscore object for **CommonJS**, with backwards-compatibility
|
52 | 6de88ee1 | Stauros Kroustouris | // for the old `require()` API. If we're not in CommonJS, add `_` to the
|
53 | 6de88ee1 | Stauros Kroustouris | // global object.
|
54 | 6de88ee1 | Stauros Kroustouris | if (typeof module !== 'undefined' && module.exports) { |
55 | 6de88ee1 | Stauros Kroustouris | module.exports = _; |
56 | 6de88ee1 | Stauros Kroustouris | _._ = _; |
57 | 6de88ee1 | Stauros Kroustouris | } else {
|
58 | 6de88ee1 | Stauros Kroustouris | root._ = _; |
59 | 6de88ee1 | Stauros Kroustouris | } |
60 | 6de88ee1 | Stauros Kroustouris | |
61 | 6de88ee1 | Stauros Kroustouris | // Current version.
|
62 | 6de88ee1 | Stauros Kroustouris | _.VERSION = '1.1.6';
|
63 | 6de88ee1 | Stauros Kroustouris | |
64 | 6de88ee1 | Stauros Kroustouris | // Collection Functions
|
65 | 6de88ee1 | Stauros Kroustouris | // --------------------
|
66 | 6de88ee1 | Stauros Kroustouris | |
67 | 6de88ee1 | Stauros Kroustouris | // The cornerstone, an `each` implementation, aka `forEach`.
|
68 | 6de88ee1 | Stauros Kroustouris | // Handles objects implementing `forEach`, arrays, and raw objects.
|
69 | 6de88ee1 | Stauros Kroustouris | // Delegates to **ECMAScript 5**'s native `forEach` if available.
|
70 | 6de88ee1 | Stauros Kroustouris | var each = _.each = _.forEach = function(obj, iterator, context) { |
71 | 6de88ee1 | Stauros Kroustouris | if (obj == null) return; |
72 | 6de88ee1 | Stauros Kroustouris | if (nativeForEach && obj.forEach === nativeForEach) {
|
73 | 6de88ee1 | Stauros Kroustouris | obj.forEach(iterator, context); |
74 | 6de88ee1 | Stauros Kroustouris | } else if (_.isNumber(obj.length)) { |
75 | 6de88ee1 | Stauros Kroustouris | for (var i = 0, l = obj.length; i < l; i++) { |
76 | 6de88ee1 | Stauros Kroustouris | if (iterator.call(context, obj[i], i, obj) === breaker) return; |
77 | 6de88ee1 | Stauros Kroustouris | } |
78 | 6de88ee1 | Stauros Kroustouris | } else {
|
79 | 6de88ee1 | Stauros Kroustouris | for (var key in obj) { |
80 | 6de88ee1 | Stauros Kroustouris | if (hasOwnProperty.call(obj, key)) {
|
81 | 6de88ee1 | Stauros Kroustouris | if (iterator.call(context, obj[key], key, obj) === breaker) return; |
82 | 6de88ee1 | Stauros Kroustouris | } |
83 | 6de88ee1 | Stauros Kroustouris | } |
84 | 6de88ee1 | Stauros Kroustouris | } |
85 | 6de88ee1 | Stauros Kroustouris | }; |
86 | 6de88ee1 | Stauros Kroustouris | |
87 | 6de88ee1 | Stauros Kroustouris | // Return the results of applying the iterator to each element.
|
88 | 6de88ee1 | Stauros Kroustouris | // Delegates to **ECMAScript 5**'s native `map` if available.
|
89 | 6de88ee1 | Stauros Kroustouris | _.map = function(obj, iterator, context) { |
90 | 6de88ee1 | Stauros Kroustouris | var results = [];
|
91 | 6de88ee1 | Stauros Kroustouris | if (obj == null) return results; |
92 | 6de88ee1 | Stauros Kroustouris | if (nativeMap && obj.map === nativeMap) return obj.map(iterator, context); |
93 | 6de88ee1 | Stauros Kroustouris | each(obj, function(value, index, list) {
|
94 | 6de88ee1 | Stauros Kroustouris | results[results.length] = iterator.call(context, value, index, list); |
95 | 6de88ee1 | Stauros Kroustouris | }); |
96 | 6de88ee1 | Stauros Kroustouris | return results;
|
97 | 6de88ee1 | Stauros Kroustouris | }; |
98 | 6de88ee1 | Stauros Kroustouris | |
99 | 6de88ee1 | Stauros Kroustouris | // **Reduce** builds up a single result from a list of values, aka `inject`,
|
100 | 6de88ee1 | Stauros Kroustouris | // or `foldl`. Delegates to **ECMAScript 5**'s native `reduce` if available.
|
101 | 6de88ee1 | Stauros Kroustouris | _.reduce = _.foldl = _.inject = function(obj, iterator, memo, context) { |
102 | 6de88ee1 | Stauros Kroustouris | var initial = memo !== void 0; |
103 | 6de88ee1 | Stauros Kroustouris | if (obj == null) obj = []; |
104 | 6de88ee1 | Stauros Kroustouris | if (nativeReduce && obj.reduce === nativeReduce) {
|
105 | 6de88ee1 | Stauros Kroustouris | if (context) iterator = _.bind(iterator, context);
|
106 | 6de88ee1 | Stauros Kroustouris | return initial ? obj.reduce(iterator, memo) : obj.reduce(iterator);
|
107 | 6de88ee1 | Stauros Kroustouris | } |
108 | 6de88ee1 | Stauros Kroustouris | each(obj, function(value, index, list) {
|
109 | 6de88ee1 | Stauros Kroustouris | if (!initial && index === 0) { |
110 | 6de88ee1 | Stauros Kroustouris | memo = value; |
111 | 6de88ee1 | Stauros Kroustouris | initial = true;
|
112 | 6de88ee1 | Stauros Kroustouris | } else {
|
113 | 6de88ee1 | Stauros Kroustouris | memo = iterator.call(context, memo, value, index, list); |
114 | 6de88ee1 | Stauros Kroustouris | } |
115 | 6de88ee1 | Stauros Kroustouris | }); |
116 | 6de88ee1 | Stauros Kroustouris | if (!initial) throw new TypeError("Reduce of empty array with no initial value"); |
117 | 6de88ee1 | Stauros Kroustouris | return memo;
|
118 | 6de88ee1 | Stauros Kroustouris | }; |
119 | 6de88ee1 | Stauros Kroustouris | |
120 | 6de88ee1 | Stauros Kroustouris | // The right-associative version of reduce, also known as `foldr`.
|
121 | 6de88ee1 | Stauros Kroustouris | // Delegates to **ECMAScript 5**'s native `reduceRight` if available.
|
122 | 6de88ee1 | Stauros Kroustouris | _.reduceRight = _.foldr = function(obj, iterator, memo, context) { |
123 | 6de88ee1 | Stauros Kroustouris | if (obj == null) obj = []; |
124 | 6de88ee1 | Stauros Kroustouris | if (nativeReduceRight && obj.reduceRight === nativeReduceRight) {
|
125 | 6de88ee1 | Stauros Kroustouris | if (context) iterator = _.bind(iterator, context);
|
126 | 6de88ee1 | Stauros Kroustouris | return memo !== void 0 ? obj.reduceRight(iterator, memo) : obj.reduceRight(iterator); |
127 | 6de88ee1 | Stauros Kroustouris | } |
128 | 6de88ee1 | Stauros Kroustouris | var reversed = (_.isArray(obj) ? obj.slice() : _.toArray(obj)).reverse();
|
129 | 6de88ee1 | Stauros Kroustouris | return _.reduce(reversed, iterator, memo, context);
|
130 | 6de88ee1 | Stauros Kroustouris | }; |
131 | 6de88ee1 | Stauros Kroustouris | |
132 | 6de88ee1 | Stauros Kroustouris | // Return the first value which passes a truth test. Aliased as `detect`.
|
133 | 6de88ee1 | Stauros Kroustouris | _.find = _.detect = function(obj, iterator, context) { |
134 | 6de88ee1 | Stauros Kroustouris | var result;
|
135 | 6de88ee1 | Stauros Kroustouris | any(obj, function(value, index, list) {
|
136 | 6de88ee1 | Stauros Kroustouris | if (iterator.call(context, value, index, list)) {
|
137 | 6de88ee1 | Stauros Kroustouris | result = value; |
138 | 6de88ee1 | Stauros Kroustouris | return true; |
139 | 6de88ee1 | Stauros Kroustouris | } |
140 | 6de88ee1 | Stauros Kroustouris | }); |
141 | 6de88ee1 | Stauros Kroustouris | return result;
|
142 | 6de88ee1 | Stauros Kroustouris | }; |
143 | 6de88ee1 | Stauros Kroustouris | |
144 | 6de88ee1 | Stauros Kroustouris | // Return all the elements that pass a truth test.
|
145 | 6de88ee1 | Stauros Kroustouris | // Delegates to **ECMAScript 5**'s native `filter` if available.
|
146 | 6de88ee1 | Stauros Kroustouris | // Aliased as `select`.
|
147 | 6de88ee1 | Stauros Kroustouris | _.filter = _.select = function(obj, iterator, context) { |
148 | 6de88ee1 | Stauros Kroustouris | var results = [];
|
149 | 6de88ee1 | Stauros Kroustouris | if (obj == null) return results; |
150 | 6de88ee1 | Stauros Kroustouris | if (nativeFilter && obj.filter === nativeFilter) return obj.filter(iterator, context); |
151 | 6de88ee1 | Stauros Kroustouris | each(obj, function(value, index, list) {
|
152 | 6de88ee1 | Stauros Kroustouris | if (iterator.call(context, value, index, list)) results[results.length] = value;
|
153 | 6de88ee1 | Stauros Kroustouris | }); |
154 | 6de88ee1 | Stauros Kroustouris | return results;
|
155 | 6de88ee1 | Stauros Kroustouris | }; |
156 | 6de88ee1 | Stauros Kroustouris | |
157 | 6de88ee1 | Stauros Kroustouris | // Return all the elements for which a truth test fails.
|
158 | 6de88ee1 | Stauros Kroustouris | _.reject = function(obj, iterator, context) { |
159 | 6de88ee1 | Stauros Kroustouris | var results = [];
|
160 | 6de88ee1 | Stauros Kroustouris | if (obj == null) return results; |
161 | 6de88ee1 | Stauros Kroustouris | each(obj, function(value, index, list) {
|
162 | 6de88ee1 | Stauros Kroustouris | if (!iterator.call(context, value, index, list)) results[results.length] = value;
|
163 | 6de88ee1 | Stauros Kroustouris | }); |
164 | 6de88ee1 | Stauros Kroustouris | return results;
|
165 | 6de88ee1 | Stauros Kroustouris | }; |
166 | 6de88ee1 | Stauros Kroustouris | |
167 | 6de88ee1 | Stauros Kroustouris | // Determine whether all of the elements match a truth test.
|
168 | 6de88ee1 | Stauros Kroustouris | // Delegates to **ECMAScript 5**'s native `every` if available.
|
169 | 6de88ee1 | Stauros Kroustouris | // Aliased as `all`.
|
170 | 6de88ee1 | Stauros Kroustouris | _.every = _.all = function(obj, iterator, context) { |
171 | 6de88ee1 | Stauros Kroustouris | var result = true; |
172 | 6de88ee1 | Stauros Kroustouris | if (obj == null) return result; |
173 | 6de88ee1 | Stauros Kroustouris | if (nativeEvery && obj.every === nativeEvery) return obj.every(iterator, context); |
174 | 6de88ee1 | Stauros Kroustouris | each(obj, function(value, index, list) {
|
175 | 6de88ee1 | Stauros Kroustouris | if (!(result = result && iterator.call(context, value, index, list))) return breaker; |
176 | 6de88ee1 | Stauros Kroustouris | }); |
177 | 6de88ee1 | Stauros Kroustouris | return result;
|
178 | 6de88ee1 | Stauros Kroustouris | }; |
179 | 6de88ee1 | Stauros Kroustouris | |
180 | 6de88ee1 | Stauros Kroustouris | // Determine if at least one element in the object matches a truth test.
|
181 | 6de88ee1 | Stauros Kroustouris | // Delegates to **ECMAScript 5**'s native `some` if available.
|
182 | 6de88ee1 | Stauros Kroustouris | // Aliased as `any`.
|
183 | 6de88ee1 | Stauros Kroustouris | var any = _.some = _.any = function(obj, iterator, context) { |
184 | 6de88ee1 | Stauros Kroustouris | iterator || (iterator = _.identity); |
185 | 6de88ee1 | Stauros Kroustouris | var result = false; |
186 | 6de88ee1 | Stauros Kroustouris | if (obj == null) return result; |
187 | 6de88ee1 | Stauros Kroustouris | if (nativeSome && obj.some === nativeSome) return obj.some(iterator, context); |
188 | 6de88ee1 | Stauros Kroustouris | each(obj, function(value, index, list) {
|
189 | 6de88ee1 | Stauros Kroustouris | if (result = iterator.call(context, value, index, list)) return breaker; |
190 | 6de88ee1 | Stauros Kroustouris | }); |
191 | 6de88ee1 | Stauros Kroustouris | return result;
|
192 | 6de88ee1 | Stauros Kroustouris | }; |
193 | 6de88ee1 | Stauros Kroustouris | |
194 | 6de88ee1 | Stauros Kroustouris | // Determine if a given value is included in the array or object using `===`.
|
195 | 6de88ee1 | Stauros Kroustouris | // Aliased as `contains`.
|
196 | 6de88ee1 | Stauros Kroustouris | _.include = _.contains = function(obj, target) { |
197 | 6de88ee1 | Stauros Kroustouris | var found = false; |
198 | 6de88ee1 | Stauros Kroustouris | if (obj == null) return found; |
199 | 6de88ee1 | Stauros Kroustouris | if (nativeIndexOf && obj.indexOf === nativeIndexOf) return obj.indexOf(target) != -1; |
200 | 6de88ee1 | Stauros Kroustouris | any(obj, function(value) {
|
201 | 6de88ee1 | Stauros Kroustouris | if (found = value === target) return true; |
202 | 6de88ee1 | Stauros Kroustouris | }); |
203 | 6de88ee1 | Stauros Kroustouris | return found;
|
204 | 6de88ee1 | Stauros Kroustouris | }; |
205 | 6de88ee1 | Stauros Kroustouris | |
206 | 6de88ee1 | Stauros Kroustouris | // Invoke a method (with arguments) on every item in a collection.
|
207 | 6de88ee1 | Stauros Kroustouris | _.invoke = function(obj, method) { |
208 | 6de88ee1 | Stauros Kroustouris | var args = slice.call(arguments, 2); |
209 | 6de88ee1 | Stauros Kroustouris | return _.map(obj, function(value) { |
210 | 6de88ee1 | Stauros Kroustouris | return (method.call ? method || value : value[method]).apply(value, args);
|
211 | 6de88ee1 | Stauros Kroustouris | }); |
212 | 6de88ee1 | Stauros Kroustouris | }; |
213 | 6de88ee1 | Stauros Kroustouris | |
214 | 6de88ee1 | Stauros Kroustouris | // Convenience version of a common use case of `map`: fetching a property.
|
215 | 6de88ee1 | Stauros Kroustouris | _.pluck = function(obj, key) { |
216 | 6de88ee1 | Stauros Kroustouris | return _.map(obj, function(value){ return value[key]; }); |
217 | 6de88ee1 | Stauros Kroustouris | }; |
218 | 6de88ee1 | Stauros Kroustouris | |
219 | 6de88ee1 | Stauros Kroustouris | // Return the maximum element or (element-based computation).
|
220 | 6de88ee1 | Stauros Kroustouris | _.max = function(obj, iterator, context) { |
221 | 6de88ee1 | Stauros Kroustouris | if (!iterator && _.isArray(obj)) return Math.max.apply(Math, obj); |
222 | 6de88ee1 | Stauros Kroustouris | var result = {computed : -Infinity}; |
223 | 6de88ee1 | Stauros Kroustouris | each(obj, function(value, index, list) {
|
224 | 6de88ee1 | Stauros Kroustouris | var computed = iterator ? iterator.call(context, value, index, list) : value;
|
225 | 6de88ee1 | Stauros Kroustouris | computed >= result.computed && (result = {value : value, computed : computed}); |
226 | 6de88ee1 | Stauros Kroustouris | }); |
227 | 6de88ee1 | Stauros Kroustouris | return result.value;
|
228 | 6de88ee1 | Stauros Kroustouris | }; |
229 | 6de88ee1 | Stauros Kroustouris | |
230 | 6de88ee1 | Stauros Kroustouris | // Return the minimum element (or element-based computation).
|
231 | 6de88ee1 | Stauros Kroustouris | _.min = function(obj, iterator, context) { |
232 | 6de88ee1 | Stauros Kroustouris | if (!iterator && _.isArray(obj)) return Math.min.apply(Math, obj); |
233 | 6de88ee1 | Stauros Kroustouris | var result = {computed : Infinity}; |
234 | 6de88ee1 | Stauros Kroustouris | each(obj, function(value, index, list) {
|
235 | 6de88ee1 | Stauros Kroustouris | var computed = iterator ? iterator.call(context, value, index, list) : value;
|
236 | 6de88ee1 | Stauros Kroustouris | computed < result.computed && (result = {value : value, computed : computed}); |
237 | 6de88ee1 | Stauros Kroustouris | }); |
238 | 6de88ee1 | Stauros Kroustouris | return result.value;
|
239 | 6de88ee1 | Stauros Kroustouris | }; |
240 | 6de88ee1 | Stauros Kroustouris | |
241 | 6de88ee1 | Stauros Kroustouris | // Sort the object's values by a criterion produced by an iterator.
|
242 | 6de88ee1 | Stauros Kroustouris | _.sortBy = function(obj, iterator, context) { |
243 | 6de88ee1 | Stauros Kroustouris | return _.pluck(_.map(obj, function(value, index, list) { |
244 | 6de88ee1 | Stauros Kroustouris | return {
|
245 | 6de88ee1 | Stauros Kroustouris | value : value,
|
246 | 6de88ee1 | Stauros Kroustouris | criteria : iterator.call(context, value, index, list)
|
247 | 6de88ee1 | Stauros Kroustouris | }; |
248 | 6de88ee1 | Stauros Kroustouris | }).sort(function(left, right) {
|
249 | 6de88ee1 | Stauros Kroustouris | var a = left.criteria, b = right.criteria;
|
250 | 6de88ee1 | Stauros Kroustouris | return a < b ? -1 : a > b ? 1 : 0; |
251 | 6de88ee1 | Stauros Kroustouris | }), 'value');
|
252 | 6de88ee1 | Stauros Kroustouris | }; |
253 | 6de88ee1 | Stauros Kroustouris | |
254 | 6de88ee1 | Stauros Kroustouris | // Use a comparator function to figure out at what index an object should
|
255 | 6de88ee1 | Stauros Kroustouris | // be inserted so as to maintain order. Uses binary search.
|
256 | 6de88ee1 | Stauros Kroustouris | _.sortedIndex = function(array, obj, iterator) { |
257 | 6de88ee1 | Stauros Kroustouris | iterator || (iterator = _.identity); |
258 | 6de88ee1 | Stauros Kroustouris | var low = 0, high = array.length; |
259 | 6de88ee1 | Stauros Kroustouris | while (low < high) {
|
260 | 6de88ee1 | Stauros Kroustouris | var mid = (low + high) >> 1; |
261 | 6de88ee1 | Stauros Kroustouris | iterator(array[mid]) < iterator(obj) ? low = mid + 1 : high = mid;
|
262 | 6de88ee1 | Stauros Kroustouris | } |
263 | 6de88ee1 | Stauros Kroustouris | return low;
|
264 | 6de88ee1 | Stauros Kroustouris | }; |
265 | 6de88ee1 | Stauros Kroustouris | |
266 | 6de88ee1 | Stauros Kroustouris | // Safely convert anything iterable into a real, live array.
|
267 | 6de88ee1 | Stauros Kroustouris | _.toArray = function(iterable) { |
268 | 6de88ee1 | Stauros Kroustouris | if (!iterable) return []; |
269 | 6de88ee1 | Stauros Kroustouris | if (iterable.toArray) return iterable.toArray(); |
270 | 6de88ee1 | Stauros Kroustouris | if (_.isArray(iterable)) return iterable; |
271 | 6de88ee1 | Stauros Kroustouris | if (_.isArguments(iterable)) return slice.call(iterable); |
272 | 6de88ee1 | Stauros Kroustouris | return _.values(iterable);
|
273 | 6de88ee1 | Stauros Kroustouris | }; |
274 | 6de88ee1 | Stauros Kroustouris | |
275 | 6de88ee1 | Stauros Kroustouris | // Return the number of elements in an object.
|
276 | 6de88ee1 | Stauros Kroustouris | _.size = function(obj) { |
277 | 6de88ee1 | Stauros Kroustouris | return _.toArray(obj).length;
|
278 | 6de88ee1 | Stauros Kroustouris | }; |
279 | 6de88ee1 | Stauros Kroustouris | |
280 | 6de88ee1 | Stauros Kroustouris | // Array Functions
|
281 | 6de88ee1 | Stauros Kroustouris | // ---------------
|
282 | 6de88ee1 | Stauros Kroustouris | |
283 | 6de88ee1 | Stauros Kroustouris | // Get the first element of an array. Passing **n** will return the first N
|
284 | 6de88ee1 | Stauros Kroustouris | // values in the array. Aliased as `head`. The **guard** check allows it to work
|
285 | 6de88ee1 | Stauros Kroustouris | // with `_.map`.
|
286 | 6de88ee1 | Stauros Kroustouris | _.first = _.head = function(array, n, guard) { |
287 | 6de88ee1 | Stauros Kroustouris | return (n != null) && !guard ? slice.call(array, 0, n) : array[0]; |
288 | 6de88ee1 | Stauros Kroustouris | }; |
289 | 6de88ee1 | Stauros Kroustouris | |
290 | 6de88ee1 | Stauros Kroustouris | // Returns everything but the first entry of the array. Aliased as `tail`.
|
291 | 6de88ee1 | Stauros Kroustouris | // Especially useful on the arguments object. Passing an **index** will return
|
292 | 6de88ee1 | Stauros Kroustouris | // the rest of the values in the array from that index onward. The **guard**
|
293 | 6de88ee1 | Stauros Kroustouris | // check allows it to work with `_.map`.
|
294 | 6de88ee1 | Stauros Kroustouris | _.rest = _.tail = function(array, index, guard) { |
295 | 6de88ee1 | Stauros Kroustouris | return slice.call(array, (index == null) || guard ? 1 : index); |
296 | 6de88ee1 | Stauros Kroustouris | }; |
297 | 6de88ee1 | Stauros Kroustouris | |
298 | 6de88ee1 | Stauros Kroustouris | // Get the last element of an array.
|
299 | 6de88ee1 | Stauros Kroustouris | _.last = function(array) { |
300 | 6de88ee1 | Stauros Kroustouris | return array[array.length - 1]; |
301 | 6de88ee1 | Stauros Kroustouris | }; |
302 | 6de88ee1 | Stauros Kroustouris | |
303 | 6de88ee1 | Stauros Kroustouris | // Trim out all falsy values from an array.
|
304 | 6de88ee1 | Stauros Kroustouris | _.compact = function(array) { |
305 | 6de88ee1 | Stauros Kroustouris | return _.filter(array, function(value){ return !!value; }); |
306 | 6de88ee1 | Stauros Kroustouris | }; |
307 | 6de88ee1 | Stauros Kroustouris | |
308 | 6de88ee1 | Stauros Kroustouris | // Return a completely flattened version of an array.
|
309 | 6de88ee1 | Stauros Kroustouris | _.flatten = function(array) { |
310 | 6de88ee1 | Stauros Kroustouris | return _.reduce(array, function(memo, value) { |
311 | 6de88ee1 | Stauros Kroustouris | if (_.isArray(value)) return memo.concat(_.flatten(value)); |
312 | 6de88ee1 | Stauros Kroustouris | memo[memo.length] = value; |
313 | 6de88ee1 | Stauros Kroustouris | return memo;
|
314 | 6de88ee1 | Stauros Kroustouris | }, []); |
315 | 6de88ee1 | Stauros Kroustouris | }; |
316 | 6de88ee1 | Stauros Kroustouris | |
317 | 6de88ee1 | Stauros Kroustouris | // Return a version of the array that does not contain the specified value(s).
|
318 | 6de88ee1 | Stauros Kroustouris | _.without = function(array) { |
319 | 6de88ee1 | Stauros Kroustouris | var values = slice.call(arguments, 1); |
320 | 6de88ee1 | Stauros Kroustouris | return _.filter(array, function(value){ return !_.include(values, value); }); |
321 | 6de88ee1 | Stauros Kroustouris | }; |
322 | 6de88ee1 | Stauros Kroustouris | |
323 | 6de88ee1 | Stauros Kroustouris | // Produce a duplicate-free version of the array. If the array has already
|
324 | 6de88ee1 | Stauros Kroustouris | // been sorted, you have the option of using a faster algorithm.
|
325 | 6de88ee1 | Stauros Kroustouris | // Aliased as `unique`.
|
326 | 6de88ee1 | Stauros Kroustouris | _.uniq = _.unique = function(array, isSorted) { |
327 | 6de88ee1 | Stauros Kroustouris | return _.reduce(array, function(memo, el, i) { |
328 | 6de88ee1 | Stauros Kroustouris | if (0 == i || (isSorted === true ? _.last(memo) != el : !_.include(memo, el))) memo[memo.length] = el; |
329 | 6de88ee1 | Stauros Kroustouris | return memo;
|
330 | 6de88ee1 | Stauros Kroustouris | }, []); |
331 | 6de88ee1 | Stauros Kroustouris | }; |
332 | 6de88ee1 | Stauros Kroustouris | |
333 | 6de88ee1 | Stauros Kroustouris | // Produce an array that contains every item shared between all the
|
334 | 6de88ee1 | Stauros Kroustouris | // passed-in arrays.
|
335 | 6de88ee1 | Stauros Kroustouris | _.intersect = function(array) { |
336 | 6de88ee1 | Stauros Kroustouris | var rest = slice.call(arguments, 1); |
337 | 6de88ee1 | Stauros Kroustouris | return _.filter(_.uniq(array), function(item) { |
338 | 6de88ee1 | Stauros Kroustouris | return _.every(rest, function(other) { |
339 | 6de88ee1 | Stauros Kroustouris | return _.indexOf(other, item) >= 0; |
340 | 6de88ee1 | Stauros Kroustouris | }); |
341 | 6de88ee1 | Stauros Kroustouris | }); |
342 | 6de88ee1 | Stauros Kroustouris | }; |
343 | 6de88ee1 | Stauros Kroustouris | |
344 | 6de88ee1 | Stauros Kroustouris | // Zip together multiple lists into a single array -- elements that share
|
345 | 6de88ee1 | Stauros Kroustouris | // an index go together.
|
346 | 6de88ee1 | Stauros Kroustouris | _.zip = function() { |
347 | 6de88ee1 | Stauros Kroustouris | var args = slice.call(arguments); |
348 | 6de88ee1 | Stauros Kroustouris | var length = _.max(_.pluck(args, 'length')); |
349 | 6de88ee1 | Stauros Kroustouris | var results = new Array(length); |
350 | 6de88ee1 | Stauros Kroustouris | for (var i = 0; i < length; i++) results[i] = _.pluck(args, "" + i); |
351 | 6de88ee1 | Stauros Kroustouris | return results;
|
352 | 6de88ee1 | Stauros Kroustouris | }; |
353 | 6de88ee1 | Stauros Kroustouris | |
354 | 6de88ee1 | Stauros Kroustouris | // If the browser doesn't supply us with indexOf (I'm looking at you, **MSIE**),
|
355 | 6de88ee1 | Stauros Kroustouris | // we need this function. Return the position of the first occurrence of an
|
356 | 6de88ee1 | Stauros Kroustouris | // item in an array, or -1 if the item is not included in the array.
|
357 | 6de88ee1 | Stauros Kroustouris | // Delegates to **ECMAScript 5**'s native `indexOf` if available.
|
358 | 6de88ee1 | Stauros Kroustouris | // If the array is large and already in sort order, pass `true`
|
359 | 6de88ee1 | Stauros Kroustouris | // for **isSorted** to use binary search.
|
360 | 6de88ee1 | Stauros Kroustouris | _.indexOf = function(array, item, isSorted) { |
361 | 6de88ee1 | Stauros Kroustouris | if (array == null) return -1; |
362 | 6de88ee1 | Stauros Kroustouris | var i, l;
|
363 | 6de88ee1 | Stauros Kroustouris | if (isSorted) {
|
364 | 6de88ee1 | Stauros Kroustouris | i = _.sortedIndex(array, item); |
365 | 6de88ee1 | Stauros Kroustouris | return array[i] === item ? i : -1; |
366 | 6de88ee1 | Stauros Kroustouris | } |
367 | 6de88ee1 | Stauros Kroustouris | if (nativeIndexOf && array.indexOf === nativeIndexOf) return array.indexOf(item); |
368 | 6de88ee1 | Stauros Kroustouris | for (i = 0, l = array.length; i < l; i++) if (array[i] === item) return i; |
369 | 6de88ee1 | Stauros Kroustouris | return -1; |
370 | 6de88ee1 | Stauros Kroustouris | }; |
371 | 6de88ee1 | Stauros Kroustouris | |
372 | 6de88ee1 | Stauros Kroustouris | |
373 | 6de88ee1 | Stauros Kroustouris | // Delegates to **ECMAScript 5**'s native `lastIndexOf` if available.
|
374 | 6de88ee1 | Stauros Kroustouris | _.lastIndexOf = function(array, item) { |
375 | 6de88ee1 | Stauros Kroustouris | if (array == null) return -1; |
376 | 6de88ee1 | Stauros Kroustouris | if (nativeLastIndexOf && array.lastIndexOf === nativeLastIndexOf) return array.lastIndexOf(item); |
377 | 6de88ee1 | Stauros Kroustouris | var i = array.length;
|
378 | 6de88ee1 | Stauros Kroustouris | while (i--) if (array[i] === item) return i; |
379 | 6de88ee1 | Stauros Kroustouris | return -1; |
380 | 6de88ee1 | Stauros Kroustouris | }; |
381 | 6de88ee1 | Stauros Kroustouris | |
382 | 6de88ee1 | Stauros Kroustouris | // Generate an integer Array containing an arithmetic progression. A port of
|
383 | 6de88ee1 | Stauros Kroustouris | // the native Python `range()` function. See
|
384 | 6de88ee1 | Stauros Kroustouris | // [the Python documentation](http://docs.python.org/library/functions.html#range).
|
385 | 6de88ee1 | Stauros Kroustouris | _.range = function(start, stop, step) { |
386 | 6de88ee1 | Stauros Kroustouris | if (arguments.length <= 1) { |
387 | 6de88ee1 | Stauros Kroustouris | stop = start || 0;
|
388 | 6de88ee1 | Stauros Kroustouris | start = 0;
|
389 | 6de88ee1 | Stauros Kroustouris | } |
390 | 6de88ee1 | Stauros Kroustouris | step = arguments[2] || 1; |
391 | 6de88ee1 | Stauros Kroustouris | |
392 | 6de88ee1 | Stauros Kroustouris | var len = Math.max(Math.ceil((stop - start) / step), 0); |
393 | 6de88ee1 | Stauros Kroustouris | var idx = 0; |
394 | 6de88ee1 | Stauros Kroustouris | var range = new Array(len); |
395 | 6de88ee1 | Stauros Kroustouris | |
396 | 6de88ee1 | Stauros Kroustouris | while(idx < len) {
|
397 | 6de88ee1 | Stauros Kroustouris | range[idx++] = start; |
398 | 6de88ee1 | Stauros Kroustouris | start += step; |
399 | 6de88ee1 | Stauros Kroustouris | } |
400 | 6de88ee1 | Stauros Kroustouris | |
401 | 6de88ee1 | Stauros Kroustouris | return range;
|
402 | 6de88ee1 | Stauros Kroustouris | }; |
403 | 6de88ee1 | Stauros Kroustouris | |
404 | 6de88ee1 | Stauros Kroustouris | // Function (ahem) Functions
|
405 | 6de88ee1 | Stauros Kroustouris | // ------------------
|
406 | 6de88ee1 | Stauros Kroustouris | |
407 | 6de88ee1 | Stauros Kroustouris | // Create a function bound to a given object (assigning `this`, and arguments,
|
408 | 6de88ee1 | Stauros Kroustouris | // optionally). Binding with arguments is also known as `curry`.
|
409 | 6de88ee1 | Stauros Kroustouris | // Delegates to **ECMAScript 5**'s native `Function.bind` if available.
|
410 | 6de88ee1 | Stauros Kroustouris | // We check for `func.bind` first, to fail fast when `func` is undefined.
|
411 | 6de88ee1 | Stauros Kroustouris | _.bind = function(func, obj) { |
412 | 6de88ee1 | Stauros Kroustouris | if (func.bind === nativeBind && nativeBind) return nativeBind.apply(func, slice.call(arguments, 1)); |
413 | 6de88ee1 | Stauros Kroustouris | var args = slice.call(arguments, 2); |
414 | 6de88ee1 | Stauros Kroustouris | return function() { |
415 | 6de88ee1 | Stauros Kroustouris | return func.apply(obj, args.concat(slice.call(arguments))); |
416 | 6de88ee1 | Stauros Kroustouris | }; |
417 | 6de88ee1 | Stauros Kroustouris | }; |
418 | 6de88ee1 | Stauros Kroustouris | |
419 | 6de88ee1 | Stauros Kroustouris | // Bind all of an object's methods to that object. Useful for ensuring that
|
420 | 6de88ee1 | Stauros Kroustouris | // all callbacks defined on an object belong to it.
|
421 | 6de88ee1 | Stauros Kroustouris | _.bindAll = function(obj) { |
422 | 6de88ee1 | Stauros Kroustouris | var funcs = slice.call(arguments, 1); |
423 | 6de88ee1 | Stauros Kroustouris | if (funcs.length == 0) funcs = _.functions(obj); |
424 | 6de88ee1 | Stauros Kroustouris | each(funcs, function(f) { obj[f] = _.bind(obj[f], obj); });
|
425 | 6de88ee1 | Stauros Kroustouris | return obj;
|
426 | 6de88ee1 | Stauros Kroustouris | }; |
427 | 6de88ee1 | Stauros Kroustouris | |
428 | 6de88ee1 | Stauros Kroustouris | // Memoize an expensive function by storing its results.
|
429 | 6de88ee1 | Stauros Kroustouris | _.memoize = function(func, hasher) { |
430 | 6de88ee1 | Stauros Kroustouris | var memo = {};
|
431 | 6de88ee1 | Stauros Kroustouris | hasher || (hasher = _.identity); |
432 | 6de88ee1 | Stauros Kroustouris | return function() { |
433 | 6de88ee1 | Stauros Kroustouris | var key = hasher.apply(this, arguments); |
434 | 6de88ee1 | Stauros Kroustouris | return hasOwnProperty.call(memo, key) ? memo[key] : (memo[key] = func.apply(this, arguments)); |
435 | 6de88ee1 | Stauros Kroustouris | }; |
436 | 6de88ee1 | Stauros Kroustouris | }; |
437 | 6de88ee1 | Stauros Kroustouris | |
438 | 6de88ee1 | Stauros Kroustouris | // Delays a function for the given number of milliseconds, and then calls
|
439 | 6de88ee1 | Stauros Kroustouris | // it with the arguments supplied.
|
440 | 6de88ee1 | Stauros Kroustouris | _.delay = function(func, wait) { |
441 | 6de88ee1 | Stauros Kroustouris | var args = slice.call(arguments, 2); |
442 | 6de88ee1 | Stauros Kroustouris | return setTimeout(function(){ return func.apply(func, args); }, wait); |
443 | 6de88ee1 | Stauros Kroustouris | }; |
444 | 6de88ee1 | Stauros Kroustouris | |
445 | 6de88ee1 | Stauros Kroustouris | // Defers a function, scheduling it to run after the current call stack has
|
446 | 6de88ee1 | Stauros Kroustouris | // cleared.
|
447 | 6de88ee1 | Stauros Kroustouris | _.defer = function(func) { |
448 | 6de88ee1 | Stauros Kroustouris | return _.delay.apply(_, [func, 1].concat(slice.call(arguments, 1))); |
449 | 6de88ee1 | Stauros Kroustouris | }; |
450 | 6de88ee1 | Stauros Kroustouris | |
451 | 6de88ee1 | Stauros Kroustouris | // Internal function used to implement `_.throttle` and `_.debounce`.
|
452 | 6de88ee1 | Stauros Kroustouris | var limit = function(func, wait, debounce) { |
453 | 6de88ee1 | Stauros Kroustouris | var timeout;
|
454 | 6de88ee1 | Stauros Kroustouris | return function() { |
455 | 6de88ee1 | Stauros Kroustouris | var context = this, args = arguments; |
456 | 6de88ee1 | Stauros Kroustouris | var throttler = function() { |
457 | 6de88ee1 | Stauros Kroustouris | timeout = null;
|
458 | 6de88ee1 | Stauros Kroustouris | func.apply(context, args); |
459 | 6de88ee1 | Stauros Kroustouris | }; |
460 | 6de88ee1 | Stauros Kroustouris | if (debounce) clearTimeout(timeout);
|
461 | 6de88ee1 | Stauros Kroustouris | if (debounce || !timeout) timeout = setTimeout(throttler, wait);
|
462 | 6de88ee1 | Stauros Kroustouris | }; |
463 | 6de88ee1 | Stauros Kroustouris | }; |
464 | 6de88ee1 | Stauros Kroustouris | |
465 | 6de88ee1 | Stauros Kroustouris | // Returns a function, that, when invoked, will only be triggered at most once
|
466 | 6de88ee1 | Stauros Kroustouris | // during a given window of time.
|
467 | 6de88ee1 | Stauros Kroustouris | _.throttle = function(func, wait) { |
468 | 6de88ee1 | Stauros Kroustouris | return limit(func, wait, false); |
469 | 6de88ee1 | Stauros Kroustouris | }; |
470 | 6de88ee1 | Stauros Kroustouris | |
471 | 6de88ee1 | Stauros Kroustouris | // Returns a function, that, as long as it continues to be invoked, will not
|
472 | 6de88ee1 | Stauros Kroustouris | // be triggered. The function will be called after it stops being called for
|
473 | 6de88ee1 | Stauros Kroustouris | // N milliseconds.
|
474 | 6de88ee1 | Stauros Kroustouris | _.debounce = function(func, wait) { |
475 | 6de88ee1 | Stauros Kroustouris | return limit(func, wait, true); |
476 | 6de88ee1 | Stauros Kroustouris | }; |
477 | 6de88ee1 | Stauros Kroustouris | |
478 | 6de88ee1 | Stauros Kroustouris | // Returns a function that will be executed at most one time, no matter how
|
479 | 6de88ee1 | Stauros Kroustouris | // often you call it. Useful for lazy initialization.
|
480 | 6de88ee1 | Stauros Kroustouris | _.once = function(func) { |
481 | 6de88ee1 | Stauros Kroustouris | var ran = false, memo; |
482 | 6de88ee1 | Stauros Kroustouris | return function() { |
483 | 6de88ee1 | Stauros Kroustouris | if (ran) return memo; |
484 | 6de88ee1 | Stauros Kroustouris | ran = true;
|
485 | 6de88ee1 | Stauros Kroustouris | return memo = func.apply(this, arguments); |
486 | 6de88ee1 | Stauros Kroustouris | }; |
487 | 6de88ee1 | Stauros Kroustouris | }; |
488 | 6de88ee1 | Stauros Kroustouris | |
489 | 6de88ee1 | Stauros Kroustouris | // Returns the first function passed as an argument to the second,
|
490 | 6de88ee1 | Stauros Kroustouris | // allowing you to adjust arguments, run code before and after, and
|
491 | 6de88ee1 | Stauros Kroustouris | // conditionally execute the original function.
|
492 | 6de88ee1 | Stauros Kroustouris | _.wrap = function(func, wrapper) { |
493 | 6de88ee1 | Stauros Kroustouris | return function() { |
494 | 6de88ee1 | Stauros Kroustouris | var args = [func].concat(slice.call(arguments)); |
495 | 6de88ee1 | Stauros Kroustouris | return wrapper.apply(this, args); |
496 | 6de88ee1 | Stauros Kroustouris | }; |
497 | 6de88ee1 | Stauros Kroustouris | }; |
498 | 6de88ee1 | Stauros Kroustouris | |
499 | 6de88ee1 | Stauros Kroustouris | // Returns a function that is the composition of a list of functions, each
|
500 | 6de88ee1 | Stauros Kroustouris | // consuming the return value of the function that follows.
|
501 | 6de88ee1 | Stauros Kroustouris | _.compose = function() { |
502 | 6de88ee1 | Stauros Kroustouris | var funcs = slice.call(arguments); |
503 | 6de88ee1 | Stauros Kroustouris | return function() { |
504 | 6de88ee1 | Stauros Kroustouris | var args = slice.call(arguments); |
505 | 6de88ee1 | Stauros Kroustouris | for (var i=funcs.length-1; i >= 0; i--) { |
506 | 6de88ee1 | Stauros Kroustouris | args = [funcs[i].apply(this, args)];
|
507 | 6de88ee1 | Stauros Kroustouris | } |
508 | 6de88ee1 | Stauros Kroustouris | return args[0]; |
509 | 6de88ee1 | Stauros Kroustouris | }; |
510 | 6de88ee1 | Stauros Kroustouris | }; |
511 | 6de88ee1 | Stauros Kroustouris | |
512 | 6de88ee1 | Stauros Kroustouris | // Returns a function that will only be executed after being called N times.
|
513 | 6de88ee1 | Stauros Kroustouris | _.after = function(times, func) { |
514 | 6de88ee1 | Stauros Kroustouris | return function() { |
515 | 6de88ee1 | Stauros Kroustouris | if (--times < 1) { return func.apply(this, arguments); } |
516 | 6de88ee1 | Stauros Kroustouris | }; |
517 | 6de88ee1 | Stauros Kroustouris | }; |
518 | 6de88ee1 | Stauros Kroustouris | |
519 | 6de88ee1 | Stauros Kroustouris | |
520 | 6de88ee1 | Stauros Kroustouris | // Object Functions
|
521 | 6de88ee1 | Stauros Kroustouris | // ----------------
|
522 | 6de88ee1 | Stauros Kroustouris | |
523 | 6de88ee1 | Stauros Kroustouris | // Retrieve the names of an object's properties.
|
524 | 6de88ee1 | Stauros Kroustouris | // Delegates to **ECMAScript 5**'s native `Object.keys`
|
525 | 6de88ee1 | Stauros Kroustouris | _.keys = nativeKeys || function(obj) {
|
526 | 6de88ee1 | Stauros Kroustouris | if (obj !== Object(obj)) throw new TypeError('Invalid object'); |
527 | 6de88ee1 | Stauros Kroustouris | var keys = [];
|
528 | 6de88ee1 | Stauros Kroustouris | for (var key in obj) if (hasOwnProperty.call(obj, key)) keys[keys.length] = key; |
529 | 6de88ee1 | Stauros Kroustouris | return keys;
|
530 | 6de88ee1 | Stauros Kroustouris | }; |
531 | 6de88ee1 | Stauros Kroustouris | |
532 | 6de88ee1 | Stauros Kroustouris | // Retrieve the values of an object's properties.
|
533 | 6de88ee1 | Stauros Kroustouris | _.values = function(obj) { |
534 | 6de88ee1 | Stauros Kroustouris | return _.map(obj, _.identity);
|
535 | 6de88ee1 | Stauros Kroustouris | }; |
536 | 6de88ee1 | Stauros Kroustouris | |
537 | 6de88ee1 | Stauros Kroustouris | // Return a sorted list of the function names available on the object.
|
538 | 6de88ee1 | Stauros Kroustouris | // Aliased as `methods`
|
539 | 6de88ee1 | Stauros Kroustouris | _.functions = _.methods = function(obj) { |
540 | 6de88ee1 | Stauros Kroustouris | return _.filter(_.keys(obj), function(key){ return _.isFunction(obj[key]); }).sort(); |
541 | 6de88ee1 | Stauros Kroustouris | }; |
542 | 6de88ee1 | Stauros Kroustouris | |
543 | 6de88ee1 | Stauros Kroustouris | // Extend a given object with all the properties in passed-in object(s).
|
544 | 6de88ee1 | Stauros Kroustouris | _.extend = function(obj) { |
545 | 6de88ee1 | Stauros Kroustouris | each(slice.call(arguments, 1), function(source) { |
546 | 6de88ee1 | Stauros Kroustouris | for (var prop in source) { |
547 | 6de88ee1 | Stauros Kroustouris | if (source[prop] !== void 0) obj[prop] = source[prop]; |
548 | 6de88ee1 | Stauros Kroustouris | } |
549 | 6de88ee1 | Stauros Kroustouris | }); |
550 | 6de88ee1 | Stauros Kroustouris | return obj;
|
551 | 6de88ee1 | Stauros Kroustouris | }; |
552 | 6de88ee1 | Stauros Kroustouris | |
553 | 6de88ee1 | Stauros Kroustouris | // Fill in a given object with default properties.
|
554 | 6de88ee1 | Stauros Kroustouris | _.defaults = function(obj) { |
555 | 6de88ee1 | Stauros Kroustouris | each(slice.call(arguments, 1), function(source) { |
556 | 6de88ee1 | Stauros Kroustouris | for (var prop in source) { |
557 | 6de88ee1 | Stauros Kroustouris | if (obj[prop] == null) obj[prop] = source[prop]; |
558 | 6de88ee1 | Stauros Kroustouris | } |
559 | 6de88ee1 | Stauros Kroustouris | }); |
560 | 6de88ee1 | Stauros Kroustouris | return obj;
|
561 | 6de88ee1 | Stauros Kroustouris | }; |
562 | 6de88ee1 | Stauros Kroustouris | |
563 | 6de88ee1 | Stauros Kroustouris | // Create a (shallow-cloned) duplicate of an object.
|
564 | 6de88ee1 | Stauros Kroustouris | _.clone = function(obj) { |
565 | 6de88ee1 | Stauros Kroustouris | return _.isArray(obj) ? obj.slice() : _.extend({}, obj);
|
566 | 6de88ee1 | Stauros Kroustouris | }; |
567 | 6de88ee1 | Stauros Kroustouris | |
568 | 6de88ee1 | Stauros Kroustouris | // Invokes interceptor with the obj, and then returns obj.
|
569 | 6de88ee1 | Stauros Kroustouris | // The primary purpose of this method is to "tap into" a method chain, in
|
570 | 6de88ee1 | Stauros Kroustouris | // order to perform operations on intermediate results within the chain.
|
571 | 6de88ee1 | Stauros Kroustouris | _.tap = function(obj, interceptor) { |
572 | 6de88ee1 | Stauros Kroustouris | interceptor(obj); |
573 | 6de88ee1 | Stauros Kroustouris | return obj;
|
574 | 6de88ee1 | Stauros Kroustouris | }; |
575 | 6de88ee1 | Stauros Kroustouris | |
576 | 6de88ee1 | Stauros Kroustouris | // Perform a deep comparison to check if two objects are equal.
|
577 | 6de88ee1 | Stauros Kroustouris | _.isEqual = function(a, b) { |
578 | 6de88ee1 | Stauros Kroustouris | // Check object identity.
|
579 | 6de88ee1 | Stauros Kroustouris | if (a === b) return true; |
580 | 6de88ee1 | Stauros Kroustouris | // Different types?
|
581 | 6de88ee1 | Stauros Kroustouris | var atype = typeof(a), btype = typeof(b); |
582 | 6de88ee1 | Stauros Kroustouris | if (atype != btype) return false; |
583 | 6de88ee1 | Stauros Kroustouris | // Basic equality test (watch out for coercions).
|
584 | 6de88ee1 | Stauros Kroustouris | if (a == b) return true; |
585 | 6de88ee1 | Stauros Kroustouris | // One is falsy and the other truthy.
|
586 | 6de88ee1 | Stauros Kroustouris | if ((!a && b) || (a && !b)) return false; |
587 | 6de88ee1 | Stauros Kroustouris | // Unwrap any wrapped objects.
|
588 | 6de88ee1 | Stauros Kroustouris | if (a._chain) a = a._wrapped;
|
589 | 6de88ee1 | Stauros Kroustouris | if (b._chain) b = b._wrapped;
|
590 | 6de88ee1 | Stauros Kroustouris | // One of them implements an isEqual()?
|
591 | 6de88ee1 | Stauros Kroustouris | if (a.isEqual) return a.isEqual(b); |
592 | 6de88ee1 | Stauros Kroustouris | // Check dates' integer values.
|
593 | 6de88ee1 | Stauros Kroustouris | if (_.isDate(a) && _.isDate(b)) return a.getTime() === b.getTime(); |
594 | 6de88ee1 | Stauros Kroustouris | // Both are NaN?
|
595 | 6de88ee1 | Stauros Kroustouris | if (_.isNaN(a) && _.isNaN(b)) return false; |
596 | 6de88ee1 | Stauros Kroustouris | // Compare regular expressions.
|
597 | 6de88ee1 | Stauros Kroustouris | if (_.isRegExp(a) && _.isRegExp(b))
|
598 | 6de88ee1 | Stauros Kroustouris | return a.source === b.source &&
|
599 | 6de88ee1 | Stauros Kroustouris | a.global === b.global && |
600 | 6de88ee1 | Stauros Kroustouris | a.ignoreCase === b.ignoreCase && |
601 | 6de88ee1 | Stauros Kroustouris | a.multiline === b.multiline; |
602 | 6de88ee1 | Stauros Kroustouris | // If a is not an object by this point, we can't handle it.
|
603 | 6de88ee1 | Stauros Kroustouris | if (atype !== 'object') return false; |
604 | 6de88ee1 | Stauros Kroustouris | // Check for different array lengths before comparing contents.
|
605 | 6de88ee1 | Stauros Kroustouris | if (a.length && (a.length !== b.length)) return false; |
606 | 6de88ee1 | Stauros Kroustouris | // Nothing else worked, deep compare the contents.
|
607 | 6de88ee1 | Stauros Kroustouris | var aKeys = _.keys(a), bKeys = _.keys(b);
|
608 | 6de88ee1 | Stauros Kroustouris | // Different object sizes?
|
609 | 6de88ee1 | Stauros Kroustouris | if (aKeys.length != bKeys.length) return false; |
610 | 6de88ee1 | Stauros Kroustouris | // Recursive comparison of contents.
|
611 | 6de88ee1 | Stauros Kroustouris | for (var key in a) if (!(key in b) || !_.isEqual(a[key], b[key])) return false; |
612 | 6de88ee1 | Stauros Kroustouris | return true; |
613 | 6de88ee1 | Stauros Kroustouris | }; |
614 | 6de88ee1 | Stauros Kroustouris | |
615 | 6de88ee1 | Stauros Kroustouris | // Is a given array or object empty?
|
616 | 6de88ee1 | Stauros Kroustouris | _.isEmpty = function(obj) { |
617 | 6de88ee1 | Stauros Kroustouris | if (_.isArray(obj) || _.isString(obj)) return obj.length === 0; |
618 | 6de88ee1 | Stauros Kroustouris | for (var key in obj) if (hasOwnProperty.call(obj, key)) return false; |
619 | 6de88ee1 | Stauros Kroustouris | return true; |
620 | 6de88ee1 | Stauros Kroustouris | }; |
621 | 6de88ee1 | Stauros Kroustouris | |
622 | 6de88ee1 | Stauros Kroustouris | // Is a given value a DOM element?
|
623 | 6de88ee1 | Stauros Kroustouris | _.isElement = function(obj) { |
624 | 6de88ee1 | Stauros Kroustouris | return !!(obj && obj.nodeType == 1); |
625 | 6de88ee1 | Stauros Kroustouris | }; |
626 | 6de88ee1 | Stauros Kroustouris | |
627 | 6de88ee1 | Stauros Kroustouris | // Is a given value an array?
|
628 | 6de88ee1 | Stauros Kroustouris | // Delegates to ECMA5's native Array.isArray
|
629 | 6de88ee1 | Stauros Kroustouris | _.isArray = nativeIsArray || function(obj) {
|
630 | 6de88ee1 | Stauros Kroustouris | return toString.call(obj) === '[object Array]'; |
631 | 6de88ee1 | Stauros Kroustouris | }; |
632 | 6de88ee1 | Stauros Kroustouris | |
633 | 6de88ee1 | Stauros Kroustouris | // Is a given variable an arguments object?
|
634 | 6de88ee1 | Stauros Kroustouris | _.isArguments = function(obj) { |
635 | 6de88ee1 | Stauros Kroustouris | return !!(obj && hasOwnProperty.call(obj, 'callee')); |
636 | 6de88ee1 | Stauros Kroustouris | }; |
637 | 6de88ee1 | Stauros Kroustouris | |
638 | 6de88ee1 | Stauros Kroustouris | // Is a given value a function?
|
639 | 6de88ee1 | Stauros Kroustouris | _.isFunction = function(obj) { |
640 | 6de88ee1 | Stauros Kroustouris | return !!(obj && obj.constructor && obj.call && obj.apply);
|
641 | 6de88ee1 | Stauros Kroustouris | }; |
642 | 6de88ee1 | Stauros Kroustouris | |
643 | 6de88ee1 | Stauros Kroustouris | // Is a given value a string?
|
644 | 6de88ee1 | Stauros Kroustouris | _.isString = function(obj) { |
645 | 6de88ee1 | Stauros Kroustouris | return !!(obj === '' || (obj && obj.charCodeAt && obj.substr)); |
646 | 6de88ee1 | Stauros Kroustouris | }; |
647 | 6de88ee1 | Stauros Kroustouris | |
648 | 6de88ee1 | Stauros Kroustouris | // Is a given value a number?
|
649 | 6de88ee1 | Stauros Kroustouris | _.isNumber = function(obj) { |
650 | 6de88ee1 | Stauros Kroustouris | return !!(obj === 0 || (obj && obj.toExponential && obj.toFixed)); |
651 | 6de88ee1 | Stauros Kroustouris | }; |
652 | 6de88ee1 | Stauros Kroustouris | |
653 | 6de88ee1 | Stauros Kroustouris | // Is the given value `NaN`? `NaN` happens to be the only value in JavaScript
|
654 | 6de88ee1 | Stauros Kroustouris | // that does not equal itself.
|
655 | 6de88ee1 | Stauros Kroustouris | _.isNaN = function(obj) { |
656 | 6de88ee1 | Stauros Kroustouris | return obj !== obj;
|
657 | 6de88ee1 | Stauros Kroustouris | }; |
658 | 6de88ee1 | Stauros Kroustouris | |
659 | 6de88ee1 | Stauros Kroustouris | // Is a given value a boolean?
|
660 | 6de88ee1 | Stauros Kroustouris | _.isBoolean = function(obj) { |
661 | 6de88ee1 | Stauros Kroustouris | return obj === true || obj === false; |
662 | 6de88ee1 | Stauros Kroustouris | }; |
663 | 6de88ee1 | Stauros Kroustouris | |
664 | 6de88ee1 | Stauros Kroustouris | // Is a given value a date?
|
665 | 6de88ee1 | Stauros Kroustouris | _.isDate = function(obj) { |
666 | 6de88ee1 | Stauros Kroustouris | return !!(obj && obj.getTimezoneOffset && obj.setUTCFullYear);
|
667 | 6de88ee1 | Stauros Kroustouris | }; |
668 | 6de88ee1 | Stauros Kroustouris | |
669 | 6de88ee1 | Stauros Kroustouris | // Is the given value a regular expression?
|
670 | 6de88ee1 | Stauros Kroustouris | _.isRegExp = function(obj) { |
671 | 6de88ee1 | Stauros Kroustouris | return !!(obj && obj.test && obj.exec && (obj.ignoreCase || obj.ignoreCase === false)); |
672 | 6de88ee1 | Stauros Kroustouris | }; |
673 | 6de88ee1 | Stauros Kroustouris | |
674 | 6de88ee1 | Stauros Kroustouris | // Is a given value equal to null?
|
675 | 6de88ee1 | Stauros Kroustouris | _.isNull = function(obj) { |
676 | 6de88ee1 | Stauros Kroustouris | return obj === null; |
677 | 6de88ee1 | Stauros Kroustouris | }; |
678 | 6de88ee1 | Stauros Kroustouris | |
679 | 6de88ee1 | Stauros Kroustouris | // Is a given variable undefined?
|
680 | 6de88ee1 | Stauros Kroustouris | _.isUndefined = function(obj) { |
681 | 6de88ee1 | Stauros Kroustouris | return obj === void 0; |
682 | 6de88ee1 | Stauros Kroustouris | }; |
683 | 6de88ee1 | Stauros Kroustouris | |
684 | 6de88ee1 | Stauros Kroustouris | // Utility Functions
|
685 | 6de88ee1 | Stauros Kroustouris | // -----------------
|
686 | 6de88ee1 | Stauros Kroustouris | |
687 | 6de88ee1 | Stauros Kroustouris | // Run Underscore.js in *noConflict* mode, returning the `_` variable to its
|
688 | 6de88ee1 | Stauros Kroustouris | // previous owner. Returns a reference to the Underscore object.
|
689 | 6de88ee1 | Stauros Kroustouris | _.noConflict = function() { |
690 | 6de88ee1 | Stauros Kroustouris | root._ = previousUnderscore; |
691 | 6de88ee1 | Stauros Kroustouris | return this; |
692 | 6de88ee1 | Stauros Kroustouris | }; |
693 | 6de88ee1 | Stauros Kroustouris | |
694 | 6de88ee1 | Stauros Kroustouris | // Keep the identity function around for default iterators.
|
695 | 6de88ee1 | Stauros Kroustouris | _.identity = function(value) { |
696 | 6de88ee1 | Stauros Kroustouris | return value;
|
697 | 6de88ee1 | Stauros Kroustouris | }; |
698 | 6de88ee1 | Stauros Kroustouris | |
699 | 6de88ee1 | Stauros Kroustouris | // Run a function **n** times.
|
700 | 6de88ee1 | Stauros Kroustouris | _.times = function (n, iterator, context) { |
701 | 6de88ee1 | Stauros Kroustouris | for (var i = 0; i < n; i++) iterator.call(context, i); |
702 | 6de88ee1 | Stauros Kroustouris | }; |
703 | 6de88ee1 | Stauros Kroustouris | |
704 | 6de88ee1 | Stauros Kroustouris | // Add your own custom functions to the Underscore object, ensuring that
|
705 | 6de88ee1 | Stauros Kroustouris | // they're correctly added to the OOP wrapper as well.
|
706 | 6de88ee1 | Stauros Kroustouris | _.mixin = function(obj) { |
707 | 6de88ee1 | Stauros Kroustouris | each(_.functions(obj), function(name){
|
708 | 6de88ee1 | Stauros Kroustouris | addToWrapper(name, _[name] = obj[name]); |
709 | 6de88ee1 | Stauros Kroustouris | }); |
710 | 6de88ee1 | Stauros Kroustouris | }; |
711 | 6de88ee1 | Stauros Kroustouris | |
712 | 6de88ee1 | Stauros Kroustouris | // Generate a unique integer id (unique within the entire client session).
|
713 | 6de88ee1 | Stauros Kroustouris | // Useful for temporary DOM ids.
|
714 | 6de88ee1 | Stauros Kroustouris | var idCounter = 0; |
715 | 6de88ee1 | Stauros Kroustouris | _.uniqueId = function(prefix) { |
716 | 6de88ee1 | Stauros Kroustouris | var id = idCounter++;
|
717 | 6de88ee1 | Stauros Kroustouris | return prefix ? prefix + id : id;
|
718 | 6de88ee1 | Stauros Kroustouris | }; |
719 | 6de88ee1 | Stauros Kroustouris | |
720 | 6de88ee1 | Stauros Kroustouris | // By default, Underscore uses ERB-style template delimiters, change the
|
721 | 6de88ee1 | Stauros Kroustouris | // following template settings to use alternative delimiters.
|
722 | 6de88ee1 | Stauros Kroustouris | _.templateSettings = { |
723 | 6de88ee1 | Stauros Kroustouris | evaluate : /<%([\s\S]+?)%>/g, |
724 | 6de88ee1 | Stauros Kroustouris | interpolate : /<%=([\s\S]+?)%>/g |
725 | 6de88ee1 | Stauros Kroustouris | }; |
726 | 6de88ee1 | Stauros Kroustouris | |
727 | 6de88ee1 | Stauros Kroustouris | // JavaScript micro-templating, similar to John Resig's implementation.
|
728 | 6de88ee1 | Stauros Kroustouris | // Underscore templating handles arbitrary delimiters, preserves whitespace,
|
729 | 6de88ee1 | Stauros Kroustouris | // and correctly escapes quotes within interpolated code.
|
730 | 6de88ee1 | Stauros Kroustouris | _.template = function(str, data) { |
731 | 6de88ee1 | Stauros Kroustouris | var c = _.templateSettings;
|
732 | 6de88ee1 | Stauros Kroustouris | var tmpl = 'var __p=[],print=function(){__p.push.apply(__p,arguments);};' + |
733 | 6de88ee1 | Stauros Kroustouris | 'with(obj||{}){__p.push(\'' +
|
734 | 6de88ee1 | Stauros Kroustouris | str.replace(/\\/g, '\\\\') |
735 | 6de88ee1 | Stauros Kroustouris | .replace(/'/g, "\\'") |
736 | 6de88ee1 | Stauros Kroustouris | .replace(c.interpolate, function(match, code) {
|
737 | 6de88ee1 | Stauros Kroustouris | return "'," + code.replace(/\\'/g, "'") + ",'"; |
738 | 6de88ee1 | Stauros Kroustouris | }) |
739 | 6de88ee1 | Stauros Kroustouris | .replace(c.evaluate || null, function(match, code) { |
740 | 6de88ee1 | Stauros Kroustouris | return "');" + code.replace(/\\'/g, "'") |
741 | 6de88ee1 | Stauros Kroustouris | .replace(/[\r\n\t]/g, ' ') + "__p.push('"; |
742 | 6de88ee1 | Stauros Kroustouris | }) |
743 | 6de88ee1 | Stauros Kroustouris | .replace(/\r/g, '\\r') |
744 | 6de88ee1 | Stauros Kroustouris | .replace(/\n/g, '\\n') |
745 | 6de88ee1 | Stauros Kroustouris | .replace(/\t/g, '\\t') |
746 | 6de88ee1 | Stauros Kroustouris | + "');}return __p.join('');";
|
747 | 6de88ee1 | Stauros Kroustouris | var func = new Function('obj', tmpl); |
748 | 6de88ee1 | Stauros Kroustouris | return data ? func(data) : func;
|
749 | 6de88ee1 | Stauros Kroustouris | }; |
750 | 6de88ee1 | Stauros Kroustouris | |
751 | 6de88ee1 | Stauros Kroustouris | // The OOP Wrapper
|
752 | 6de88ee1 | Stauros Kroustouris | // ---------------
|
753 | 6de88ee1 | Stauros Kroustouris | |
754 | 6de88ee1 | Stauros Kroustouris | // If Underscore is called as a function, it returns a wrapped object that
|
755 | 6de88ee1 | Stauros Kroustouris | // can be used OO-style. This wrapper holds altered versions of all the
|
756 | 6de88ee1 | Stauros Kroustouris | // underscore functions. Wrapped objects may be chained.
|
757 | 6de88ee1 | Stauros Kroustouris | var wrapper = function(obj) { this._wrapped = obj; }; |
758 | 6de88ee1 | Stauros Kroustouris | |
759 | 6de88ee1 | Stauros Kroustouris | // Expose `wrapper.prototype` as `_.prototype`
|
760 | 6de88ee1 | Stauros Kroustouris | _.prototype = wrapper.prototype; |
761 | 6de88ee1 | Stauros Kroustouris | |
762 | 6de88ee1 | Stauros Kroustouris | // Helper function to continue chaining intermediate results.
|
763 | 6de88ee1 | Stauros Kroustouris | var result = function(obj, chain) { |
764 | 6de88ee1 | Stauros Kroustouris | return chain ? _(obj).chain() : obj;
|
765 | 6de88ee1 | Stauros Kroustouris | }; |
766 | 6de88ee1 | Stauros Kroustouris | |
767 | 6de88ee1 | Stauros Kroustouris | // A method to easily add functions to the OOP wrapper.
|
768 | 6de88ee1 | Stauros Kroustouris | var addToWrapper = function(name, func) { |
769 | 6de88ee1 | Stauros Kroustouris | wrapper.prototype[name] = function() {
|
770 | 6de88ee1 | Stauros Kroustouris | var args = slice.call(arguments); |
771 | 6de88ee1 | Stauros Kroustouris | unshift.call(args, this._wrapped);
|
772 | 6de88ee1 | Stauros Kroustouris | return result(func.apply(_, args), this._chain); |
773 | 6de88ee1 | Stauros Kroustouris | }; |
774 | 6de88ee1 | Stauros Kroustouris | }; |
775 | 6de88ee1 | Stauros Kroustouris | |
776 | 6de88ee1 | Stauros Kroustouris | // Add all of the Underscore functions to the wrapper object.
|
777 | 6de88ee1 | Stauros Kroustouris | _.mixin(_); |
778 | 6de88ee1 | Stauros Kroustouris | |
779 | 6de88ee1 | Stauros Kroustouris | // Add all mutator Array functions to the wrapper.
|
780 | 6de88ee1 | Stauros Kroustouris | each(['pop', 'push', 'reverse', 'shift', 'sort', 'splice', 'unshift'], function(name) { |
781 | 6de88ee1 | Stauros Kroustouris | var method = ArrayProto[name];
|
782 | 6de88ee1 | Stauros Kroustouris | wrapper.prototype[name] = function() {
|
783 | 6de88ee1 | Stauros Kroustouris | method.apply(this._wrapped, arguments); |
784 | 6de88ee1 | Stauros Kroustouris | return result(this._wrapped, this._chain); |
785 | 6de88ee1 | Stauros Kroustouris | }; |
786 | 6de88ee1 | Stauros Kroustouris | }); |
787 | 6de88ee1 | Stauros Kroustouris | |
788 | 6de88ee1 | Stauros Kroustouris | // Add all accessor Array functions to the wrapper.
|
789 | 6de88ee1 | Stauros Kroustouris | each(['concat', 'join', 'slice'], function(name) { |
790 | 6de88ee1 | Stauros Kroustouris | var method = ArrayProto[name];
|
791 | 6de88ee1 | Stauros Kroustouris | wrapper.prototype[name] = function() {
|
792 | 6de88ee1 | Stauros Kroustouris | return result(method.apply(this._wrapped, arguments), this._chain); |
793 | 6de88ee1 | Stauros Kroustouris | }; |
794 | 6de88ee1 | Stauros Kroustouris | }); |
795 | 6de88ee1 | Stauros Kroustouris | |
796 | 6de88ee1 | Stauros Kroustouris | // Start chaining a wrapped Underscore object.
|
797 | 6de88ee1 | Stauros Kroustouris | wrapper.prototype.chain = function() { |
798 | 6de88ee1 | Stauros Kroustouris | this._chain = true; |
799 | 6de88ee1 | Stauros Kroustouris | return this; |
800 | 6de88ee1 | Stauros Kroustouris | }; |
801 | 6de88ee1 | Stauros Kroustouris | |
802 | 6de88ee1 | Stauros Kroustouris | // Extracts the result from a wrapped and chained object.
|
803 | 6de88ee1 | Stauros Kroustouris | wrapper.prototype.value = function() { |
804 | 6de88ee1 | Stauros Kroustouris | return this._wrapped; |
805 | 6de88ee1 | Stauros Kroustouris | }; |
806 | 6de88ee1 | Stauros Kroustouris | |
807 | 6de88ee1 | Stauros Kroustouris | })(); |