Commit | Line | Data |
---|---|---|
66f276c9 PH |
1 | // Underscore.js 1.4.4 |
2 | // http://underscorejs.org | |
3 | // (c) 2009-2013 Jeremy Ashkenas, DocumentCloud Inc. | |
4 | // Underscore may be freely distributed under the MIT license. | |
5 | ||
6 | (function() { | |
7 | ||
8 | // Baseline setup | |
9 | // -------------- | |
10 | ||
11 | // Establish the root object, `window` in the browser, or `global` on the server. | |
12 | var root = this; | |
13 | ||
14 | // Save the previous value of the `_` variable. | |
15 | var previousUnderscore = root._; | |
16 | ||
17 | // Establish the object that gets returned to break out of a loop iteration. | |
18 | var breaker = {}; | |
19 | ||
20 | // Save bytes in the minified (but not gzipped) version: | |
21 | var ArrayProto = Array.prototype, ObjProto = Object.prototype, FuncProto = Function.prototype; | |
22 | ||
23 | // Create quick reference variables for speed access to core prototypes. | |
24 | var push = ArrayProto.push, | |
25 | slice = ArrayProto.slice, | |
26 | concat = ArrayProto.concat, | |
27 | toString = ObjProto.toString, | |
28 | hasOwnProperty = ObjProto.hasOwnProperty; | |
29 | ||
30 | // All **ECMAScript 5** native function implementations that we hope to use | |
31 | // are declared here. | |
32 | var | |
33 | nativeForEach = ArrayProto.forEach, | |
34 | nativeMap = ArrayProto.map, | |
35 | nativeReduce = ArrayProto.reduce, | |
36 | nativeReduceRight = ArrayProto.reduceRight, | |
37 | nativeFilter = ArrayProto.filter, | |
38 | nativeEvery = ArrayProto.every, | |
39 | nativeSome = ArrayProto.some, | |
40 | nativeIndexOf = ArrayProto.indexOf, | |
41 | nativeLastIndexOf = ArrayProto.lastIndexOf, | |
42 | nativeIsArray = Array.isArray, | |
43 | nativeKeys = Object.keys, | |
44 | nativeBind = FuncProto.bind; | |
45 | ||
46 | // Create a safe reference to the Underscore object for use below. | |
47 | var _ = function(obj) { | |
48 | if (obj instanceof _) return obj; | |
49 | if (!(this instanceof _)) return new _(obj); | |
50 | this._wrapped = obj; | |
51 | }; | |
52 | ||
53 | // Export the Underscore object for **Node.js**, with | |
54 | // backwards-compatibility for the old `require()` API. If we're in | |
55 | // the browser, add `_` as a global object via a string identifier, | |
56 | // for Closure Compiler "advanced" mode. | |
57 | if (typeof exports !== 'undefined') { | |
58 | if (typeof module !== 'undefined' && module.exports) { | |
59 | exports = module.exports = _; | |
60 | } | |
61 | exports._ = _; | |
62 | } else { | |
63 | root._ = _; | |
64 | } | |
65 | ||
66 | // Current version. | |
67 | _.VERSION = '1.4.4'; | |
68 | ||
69 | // Collection Functions | |
70 | // -------------------- | |
71 | ||
72 | // The cornerstone, an `each` implementation, aka `forEach`. | |
73 | // Handles objects with the built-in `forEach`, arrays, and raw objects. | |
74 | // Delegates to **ECMAScript 5**'s native `forEach` if available. | |
75 | var each = _.each = _.forEach = function(obj, iterator, context) { | |
76 | if (obj == null) return; | |
77 | if (nativeForEach && obj.forEach === nativeForEach) { | |
78 | obj.forEach(iterator, context); | |
79 | } else if (obj.length === +obj.length) { | |
80 | for (var i = 0, l = obj.length; i < l; i++) { | |
81 | if (iterator.call(context, obj[i], i, obj) === breaker) return; | |
82 | } | |
83 | } else { | |
84 | for (var key in obj) { | |
85 | if (_.has(obj, key)) { | |
86 | if (iterator.call(context, obj[key], key, obj) === breaker) return; | |
87 | } | |
88 | } | |
89 | } | |
90 | }; | |
91 | ||
92 | // Return the results of applying the iterator to each element. | |
93 | // Delegates to **ECMAScript 5**'s native `map` if available. | |
94 | _.map = _.collect = function(obj, iterator, context) { | |
95 | var results = []; | |
96 | if (obj == null) return results; | |
97 | if (nativeMap && obj.map === nativeMap) return obj.map(iterator, context); | |
98 | each(obj, function(value, index, list) { | |
99 | results[results.length] = iterator.call(context, value, index, list); | |
100 | }); | |
101 | return results; | |
102 | }; | |
103 | ||
104 | var reduceError = 'Reduce of empty array with no initial value'; | |
105 | ||
106 | // **Reduce** builds up a single result from a list of values, aka `inject`, | |
107 | // or `foldl`. Delegates to **ECMAScript 5**'s native `reduce` if available. | |
108 | _.reduce = _.foldl = _.inject = function(obj, iterator, memo, context) { | |
109 | var initial = arguments.length > 2; | |
110 | if (obj == null) obj = []; | |
111 | if (nativeReduce && obj.reduce === nativeReduce) { | |
112 | if (context) iterator = _.bind(iterator, context); | |
113 | return initial ? obj.reduce(iterator, memo) : obj.reduce(iterator); | |
114 | } | |
115 | each(obj, function(value, index, list) { | |
116 | if (!initial) { | |
117 | memo = value; | |
118 | initial = true; | |
119 | } else { | |
120 | memo = iterator.call(context, memo, value, index, list); | |
121 | } | |
122 | }); | |
123 | if (!initial) throw new TypeError(reduceError); | |
124 | return memo; | |
125 | }; | |
126 | ||
127 | // The right-associative version of reduce, also known as `foldr`. | |
128 | // Delegates to **ECMAScript 5**'s native `reduceRight` if available. | |
129 | _.reduceRight = _.foldr = function(obj, iterator, memo, context) { | |
130 | var initial = arguments.length > 2; | |
131 | if (obj == null) obj = []; | |
132 | if (nativeReduceRight && obj.reduceRight === nativeReduceRight) { | |
133 | if (context) iterator = _.bind(iterator, context); | |
134 | return initial ? obj.reduceRight(iterator, memo) : obj.reduceRight(iterator); | |
135 | } | |
136 | var length = obj.length; | |
137 | if (length !== +length) { | |
138 | var keys = _.keys(obj); | |
139 | length = keys.length; | |
140 | } | |
141 | each(obj, function(value, index, list) { | |
142 | index = keys ? keys[--length] : --length; | |
143 | if (!initial) { | |
144 | memo = obj[index]; | |
145 | initial = true; | |
146 | } else { | |
147 | memo = iterator.call(context, memo, obj[index], index, list); | |
148 | } | |
149 | }); | |
150 | if (!initial) throw new TypeError(reduceError); | |
151 | return memo; | |
152 | }; | |
153 | ||
154 | // Return the first value which passes a truth test. Aliased as `detect`. | |
155 | _.find = _.detect = function(obj, iterator, context) { | |
156 | var result; | |
157 | any(obj, function(value, index, list) { | |
158 | if (iterator.call(context, value, index, list)) { | |
159 | result = value; | |
160 | return true; | |
161 | } | |
162 | }); | |
163 | return result; | |
164 | }; | |
165 | ||
166 | // Return all the elements that pass a truth test. | |
167 | // Delegates to **ECMAScript 5**'s native `filter` if available. | |
168 | // Aliased as `select`. | |
169 | _.filter = _.select = function(obj, iterator, context) { | |
170 | var results = []; | |
171 | if (obj == null) return results; | |
172 | if (nativeFilter && obj.filter === nativeFilter) return obj.filter(iterator, context); | |
173 | each(obj, function(value, index, list) { | |
174 | if (iterator.call(context, value, index, list)) results[results.length] = value; | |
175 | }); | |
176 | return results; | |
177 | }; | |
178 | ||
179 | // Return all the elements for which a truth test fails. | |
180 | _.reject = function(obj, iterator, context) { | |
181 | return _.filter(obj, function(value, index, list) { | |
182 | return !iterator.call(context, value, index, list); | |
183 | }, context); | |
184 | }; | |
185 | ||
186 | // Determine whether all of the elements match a truth test. | |
187 | // Delegates to **ECMAScript 5**'s native `every` if available. | |
188 | // Aliased as `all`. | |
189 | _.every = _.all = function(obj, iterator, context) { | |
190 | iterator || (iterator = _.identity); | |
191 | var result = true; | |
192 | if (obj == null) return result; | |
193 | if (nativeEvery && obj.every === nativeEvery) return obj.every(iterator, context); | |
194 | each(obj, function(value, index, list) { | |
195 | if (!(result = result && iterator.call(context, value, index, list))) return breaker; | |
196 | }); | |
197 | return !!result; | |
198 | }; | |
199 | ||
200 | // Determine if at least one element in the object matches a truth test. | |
201 | // Delegates to **ECMAScript 5**'s native `some` if available. | |
202 | // Aliased as `any`. | |
203 | var any = _.some = _.any = function(obj, iterator, context) { | |
204 | iterator || (iterator = _.identity); | |
205 | var result = false; | |
206 | if (obj == null) return result; | |
207 | if (nativeSome && obj.some === nativeSome) return obj.some(iterator, context); | |
208 | each(obj, function(value, index, list) { | |
209 | if (result || (result = iterator.call(context, value, index, list))) return breaker; | |
210 | }); | |
211 | return !!result; | |
212 | }; | |
213 | ||
214 | // Determine if the array or object contains a given value (using `===`). | |
215 | // Aliased as `include`. | |
216 | _.contains = _.include = function(obj, target) { | |
217 | if (obj == null) return false; | |
218 | if (nativeIndexOf && obj.indexOf === nativeIndexOf) return obj.indexOf(target) != -1; | |
219 | return any(obj, function(value) { | |
220 | return value === target; | |
221 | }); | |
222 | }; | |
223 | ||
224 | // Invoke a method (with arguments) on every item in a collection. | |
225 | _.invoke = function(obj, method) { | |
226 | var args = slice.call(arguments, 2); | |
227 | var isFunc = _.isFunction(method); | |
228 | return _.map(obj, function(value) { | |
229 | return (isFunc ? method : value[method]).apply(value, args); | |
230 | }); | |
231 | }; | |
232 | ||
233 | // Convenience version of a common use case of `map`: fetching a property. | |
234 | _.pluck = function(obj, key) { | |
235 | return _.map(obj, function(value){ return value[key]; }); | |
236 | }; | |
237 | ||
238 | // Convenience version of a common use case of `filter`: selecting only objects | |
239 | // containing specific `key:value` pairs. | |
240 | _.where = function(obj, attrs, first) { | |
241 | if (_.isEmpty(attrs)) return first ? null : []; | |
242 | return _[first ? 'find' : 'filter'](obj, function(value) { | |
243 | for (var key in attrs) { | |
244 | if (attrs[key] !== value[key]) return false; | |
245 | } | |
246 | return true; | |
247 | }); | |
248 | }; | |
249 | ||
250 | // Convenience version of a common use case of `find`: getting the first object | |
251 | // containing specific `key:value` pairs. | |
252 | _.findWhere = function(obj, attrs) { | |
253 | return _.where(obj, attrs, true); | |
254 | }; | |
255 | ||
256 | // Return the maximum element or (element-based computation). | |
257 | // Can't optimize arrays of integers longer than 65,535 elements. | |
258 | // See: https://bugs.webkit.org/show_bug.cgi?id=80797 | |
259 | _.max = function(obj, iterator, context) { | |
260 | if (!iterator && _.isArray(obj) && obj[0] === +obj[0] && obj.length < 65535) { | |
261 | return Math.max.apply(Math, obj); | |
262 | } | |
263 | if (!iterator && _.isEmpty(obj)) return -Infinity; | |
264 | var result = {computed : -Infinity, value: -Infinity}; | |
265 | each(obj, function(value, index, list) { | |
266 | var computed = iterator ? iterator.call(context, value, index, list) : value; | |
267 | computed >= result.computed && (result = {value : value, computed : computed}); | |
268 | }); | |
269 | return result.value; | |
270 | }; | |
271 | ||
272 | // Return the minimum element (or element-based computation). | |
273 | _.min = function(obj, iterator, context) { | |
274 | if (!iterator && _.isArray(obj) && obj[0] === +obj[0] && obj.length < 65535) { | |
275 | return Math.min.apply(Math, obj); | |
276 | } | |
277 | if (!iterator && _.isEmpty(obj)) return Infinity; | |
278 | var result = {computed : Infinity, value: Infinity}; | |
279 | each(obj, function(value, index, list) { | |
280 | var computed = iterator ? iterator.call(context, value, index, list) : value; | |
281 | computed < result.computed && (result = {value : value, computed : computed}); | |
282 | }); | |
283 | return result.value; | |
284 | }; | |
285 | ||
286 | // Shuffle an array. | |
287 | _.shuffle = function(obj) { | |
288 | var rand; | |
289 | var index = 0; | |
290 | var shuffled = []; | |
291 | each(obj, function(value) { | |
292 | rand = _.random(index++); | |
293 | shuffled[index - 1] = shuffled[rand]; | |
294 | shuffled[rand] = value; | |
295 | }); | |
296 | return shuffled; | |
297 | }; | |
298 | ||
299 | // An internal function to generate lookup iterators. | |
300 | var lookupIterator = function(value) { | |
301 | return _.isFunction(value) ? value : function(obj){ return obj[value]; }; | |
302 | }; | |
303 | ||
304 | // Sort the object's values by a criterion produced by an iterator. | |
305 | _.sortBy = function(obj, value, context) { | |
306 | var iterator = lookupIterator(value); | |
307 | return _.pluck(_.map(obj, function(value, index, list) { | |
308 | return { | |
309 | value : value, | |
310 | index : index, | |
311 | criteria : iterator.call(context, value, index, list) | |
312 | }; | |
313 | }).sort(function(left, right) { | |
314 | var a = left.criteria; | |
315 | var b = right.criteria; | |
316 | if (a !== b) { | |
317 | if (a > b || a === void 0) return 1; | |
318 | if (a < b || b === void 0) return -1; | |
319 | } | |
320 | return left.index < right.index ? -1 : 1; | |
321 | }), 'value'); | |
322 | }; | |
323 | ||
324 | // An internal function used for aggregate "group by" operations. | |
325 | var group = function(obj, value, context, behavior) { | |
326 | var result = {}; | |
327 | var iterator = lookupIterator(value || _.identity); | |
328 | each(obj, function(value, index) { | |
329 | var key = iterator.call(context, value, index, obj); | |
330 | behavior(result, key, value); | |
331 | }); | |
332 | return result; | |
333 | }; | |
334 | ||
335 | // Groups the object's values by a criterion. Pass either a string attribute | |
336 | // to group by, or a function that returns the criterion. | |
337 | _.groupBy = function(obj, value, context) { | |
338 | return group(obj, value, context, function(result, key, value) { | |
339 | (_.has(result, key) ? result[key] : (result[key] = [])).push(value); | |
340 | }); | |
341 | }; | |
342 | ||
343 | // Counts instances of an object that group by a certain criterion. Pass | |
344 | // either a string attribute to count by, or a function that returns the | |
345 | // criterion. | |
346 | _.countBy = function(obj, value, context) { | |
347 | return group(obj, value, context, function(result, key) { | |
348 | if (!_.has(result, key)) result[key] = 0; | |
349 | result[key]++; | |
350 | }); | |
351 | }; | |
352 | ||
353 | // Use a comparator function to figure out the smallest index at which | |
354 | // an object should be inserted so as to maintain order. Uses binary search. | |
355 | _.sortedIndex = function(array, obj, iterator, context) { | |
356 | iterator = iterator == null ? _.identity : lookupIterator(iterator); | |
357 | var value = iterator.call(context, obj); | |
358 | var low = 0, high = array.length; | |
359 | while (low < high) { | |
360 | var mid = (low + high) >>> 1; | |
361 | iterator.call(context, array[mid]) < value ? low = mid + 1 : high = mid; | |
362 | } | |
363 | return low; | |
364 | }; | |
365 | ||
366 | // Safely convert anything iterable into a real, live array. | |
367 | _.toArray = function(obj) { | |
368 | if (!obj) return []; | |
369 | if (_.isArray(obj)) return slice.call(obj); | |
370 | if (obj.length === +obj.length) return _.map(obj, _.identity); | |
371 | return _.values(obj); | |
372 | }; | |
373 | ||
374 | // Return the number of elements in an object. | |
375 | _.size = function(obj) { | |
376 | if (obj == null) return 0; | |
377 | return (obj.length === +obj.length) ? obj.length : _.keys(obj).length; | |
378 | }; | |
379 | ||
380 | // Array Functions | |
381 | // --------------- | |
382 | ||
383 | // Get the first element of an array. Passing **n** will return the first N | |
384 | // values in the array. Aliased as `head` and `take`. The **guard** check | |
385 | // allows it to work with `_.map`. | |
386 | _.first = _.head = _.take = function(array, n, guard) { | |
387 | if (array == null) return void 0; | |
388 | return (n != null) && !guard ? slice.call(array, 0, n) : array[0]; | |
389 | }; | |
390 | ||
391 | // Returns everything but the last entry of the array. Especially useful on | |
392 | // the arguments object. Passing **n** will return all the values in | |
393 | // the array, excluding the last N. The **guard** check allows it to work with | |
394 | // `_.map`. | |
395 | _.initial = function(array, n, guard) { | |
396 | return slice.call(array, 0, array.length - ((n == null) || guard ? 1 : n)); | |
397 | }; | |
398 | ||
399 | // Get the last element of an array. Passing **n** will return the last N | |
400 | // values in the array. The **guard** check allows it to work with `_.map`. | |
401 | _.last = function(array, n, guard) { | |
402 | if (array == null) return void 0; | |
403 | if ((n != null) && !guard) { | |
404 | return slice.call(array, Math.max(array.length - n, 0)); | |
405 | } else { | |
406 | return array[array.length - 1]; | |
407 | } | |
408 | }; | |
409 | ||
410 | // Returns everything but the first entry of the array. Aliased as `tail` and `drop`. | |
411 | // Especially useful on the arguments object. Passing an **n** will return | |
412 | // the rest N values in the array. The **guard** | |
413 | // check allows it to work with `_.map`. | |
414 | _.rest = _.tail = _.drop = function(array, n, guard) { | |
415 | return slice.call(array, (n == null) || guard ? 1 : n); | |
416 | }; | |
417 | ||
418 | // Trim out all falsy values from an array. | |
419 | _.compact = function(array) { | |
420 | return _.filter(array, _.identity); | |
421 | }; | |
422 | ||
423 | // Internal implementation of a recursive `flatten` function. | |
424 | var flatten = function(input, shallow, output) { | |
425 | each(input, function(value) { | |
426 | if (_.isArray(value)) { | |
427 | shallow ? push.apply(output, value) : flatten(value, shallow, output); | |
428 | } else { | |
429 | output.push(value); | |
430 | } | |
431 | }); | |
432 | return output; | |
433 | }; | |
434 | ||
435 | // Return a completely flattened version of an array. | |
436 | _.flatten = function(array, shallow) { | |
437 | return flatten(array, shallow, []); | |
438 | }; | |
439 | ||
440 | // Return a version of the array that does not contain the specified value(s). | |
441 | _.without = function(array) { | |
442 | return _.difference(array, slice.call(arguments, 1)); | |
443 | }; | |
444 | ||
445 | // Produce a duplicate-free version of the array. If the array has already | |
446 | // been sorted, you have the option of using a faster algorithm. | |
447 | // Aliased as `unique`. | |
448 | _.uniq = _.unique = function(array, isSorted, iterator, context) { | |
449 | if (_.isFunction(isSorted)) { | |
450 | context = iterator; | |
451 | iterator = isSorted; | |
452 | isSorted = false; | |
453 | } | |
454 | var initial = iterator ? _.map(array, iterator, context) : array; | |
455 | var results = []; | |
456 | var seen = []; | |
457 | each(initial, function(value, index) { | |
458 | if (isSorted ? (!index || seen[seen.length - 1] !== value) : !_.contains(seen, value)) { | |
459 | seen.push(value); | |
460 | results.push(array[index]); | |
461 | } | |
462 | }); | |
463 | return results; | |
464 | }; | |
465 | ||
466 | // Produce an array that contains the union: each distinct element from all of | |
467 | // the passed-in arrays. | |
468 | _.union = function() { | |
469 | return _.uniq(concat.apply(ArrayProto, arguments)); | |
470 | }; | |
471 | ||
472 | // Produce an array that contains every item shared between all the | |
473 | // passed-in arrays. | |
474 | _.intersection = function(array) { | |
475 | var rest = slice.call(arguments, 1); | |
476 | return _.filter(_.uniq(array), function(item) { | |
477 | return _.every(rest, function(other) { | |
478 | return _.indexOf(other, item) >= 0; | |
479 | }); | |
480 | }); | |
481 | }; | |
482 | ||
483 | // Take the difference between one array and a number of other arrays. | |
484 | // Only the elements present in just the first array will remain. | |
485 | _.difference = function(array) { | |
486 | var rest = concat.apply(ArrayProto, slice.call(arguments, 1)); | |
487 | return _.filter(array, function(value){ return !_.contains(rest, value); }); | |
488 | }; | |
489 | ||
490 | // Zip together multiple lists into a single array -- elements that share | |
491 | // an index go together. | |
492 | _.zip = function() { | |
493 | var args = slice.call(arguments); | |
494 | var length = _.max(_.pluck(args, 'length')); | |
495 | var results = new Array(length); | |
496 | for (var i = 0; i < length; i++) { | |
497 | results[i] = _.pluck(args, "" + i); | |
498 | } | |
499 | return results; | |
500 | }; | |
501 | ||
502 | // Converts lists into objects. Pass either a single array of `[key, value]` | |
503 | // pairs, or two parallel arrays of the same length -- one of keys, and one of | |
504 | // the corresponding values. | |
505 | _.object = function(list, values) { | |
506 | if (list == null) return {}; | |
507 | var result = {}; | |
508 | for (var i = 0, l = list.length; i < l; i++) { | |
509 | if (values) { | |
510 | result[list[i]] = values[i]; | |
511 | } else { | |
512 | result[list[i][0]] = list[i][1]; | |
513 | } | |
514 | } | |
515 | return result; | |
516 | }; | |
517 | ||
518 | // If the browser doesn't supply us with indexOf (I'm looking at you, **MSIE**), | |
519 | // we need this function. Return the position of the first occurrence of an | |
520 | // item in an array, or -1 if the item is not included in the array. | |
521 | // Delegates to **ECMAScript 5**'s native `indexOf` if available. | |
522 | // If the array is large and already in sort order, pass `true` | |
523 | // for **isSorted** to use binary search. | |
524 | _.indexOf = function(array, item, isSorted) { | |
525 | if (array == null) return -1; | |
526 | var i = 0, l = array.length; | |
527 | if (isSorted) { | |
528 | if (typeof isSorted == 'number') { | |
529 | i = (isSorted < 0 ? Math.max(0, l + isSorted) : isSorted); | |
530 | } else { | |
531 | i = _.sortedIndex(array, item); | |
532 | return array[i] === item ? i : -1; | |
533 | } | |
534 | } | |
535 | if (nativeIndexOf && array.indexOf === nativeIndexOf) return array.indexOf(item, isSorted); | |
536 | for (; i < l; i++) if (array[i] === item) return i; | |
537 | return -1; | |
538 | }; | |
539 | ||
540 | // Delegates to **ECMAScript 5**'s native `lastIndexOf` if available. | |
541 | _.lastIndexOf = function(array, item, from) { | |
542 | if (array == null) return -1; | |
543 | var hasIndex = from != null; | |
544 | if (nativeLastIndexOf && array.lastIndexOf === nativeLastIndexOf) { | |
545 | return hasIndex ? array.lastIndexOf(item, from) : array.lastIndexOf(item); | |
546 | } | |
547 | var i = (hasIndex ? from : array.length); | |
548 | while (i--) if (array[i] === item) return i; | |
549 | return -1; | |
550 | }; | |
551 | ||
552 | // Generate an integer Array containing an arithmetic progression. A port of | |
553 | // the native Python `range()` function. See | |
554 | // [the Python documentation](http://docs.python.org/library/functions.html#range). | |
555 | _.range = function(start, stop, step) { | |
556 | if (arguments.length <= 1) { | |
557 | stop = start || 0; | |
558 | start = 0; | |
559 | } | |
560 | step = arguments[2] || 1; | |
561 | ||
562 | var len = Math.max(Math.ceil((stop - start) / step), 0); | |
563 | var idx = 0; | |
564 | var range = new Array(len); | |
565 | ||
566 | while(idx < len) { | |
567 | range[idx++] = start; | |
568 | start += step; | |
569 | } | |
570 | ||
571 | return range; | |
572 | }; | |
573 | ||
574 | // Function (ahem) Functions | |
575 | // ------------------ | |
576 | ||
577 | // Create a function bound to a given object (assigning `this`, and arguments, | |
578 | // optionally). Delegates to **ECMAScript 5**'s native `Function.bind` if | |
579 | // available. | |
580 | _.bind = function(func, context) { | |
581 | if (func.bind === nativeBind && nativeBind) return nativeBind.apply(func, slice.call(arguments, 1)); | |
582 | var args = slice.call(arguments, 2); | |
583 | return function() { | |
584 | return func.apply(context, args.concat(slice.call(arguments))); | |
585 | }; | |
586 | }; | |
587 | ||
588 | // Partially apply a function by creating a version that has had some of its | |
589 | // arguments pre-filled, without changing its dynamic `this` context. | |
590 | _.partial = function(func) { | |
591 | var args = slice.call(arguments, 1); | |
592 | return function() { | |
593 | return func.apply(this, args.concat(slice.call(arguments))); | |
594 | }; | |
595 | }; | |
596 | ||
597 | // Bind all of an object's methods to that object. Useful for ensuring that | |
598 | // all callbacks defined on an object belong to it. | |
599 | _.bindAll = function(obj) { | |
600 | var funcs = slice.call(arguments, 1); | |
601 | if (funcs.length === 0) funcs = _.functions(obj); | |
602 | each(funcs, function(f) { obj[f] = _.bind(obj[f], obj); }); | |
603 | return obj; | |
604 | }; | |
605 | ||
606 | // Memoize an expensive function by storing its results. | |
607 | _.memoize = function(func, hasher) { | |
608 | var memo = {}; | |
609 | hasher || (hasher = _.identity); | |
610 | return function() { | |
611 | var key = hasher.apply(this, arguments); | |
612 | return _.has(memo, key) ? memo[key] : (memo[key] = func.apply(this, arguments)); | |
613 | }; | |
614 | }; | |
615 | ||
616 | // Delays a function for the given number of milliseconds, and then calls | |
617 | // it with the arguments supplied. | |
618 | _.delay = function(func, wait) { | |
619 | var args = slice.call(arguments, 2); | |
620 | return setTimeout(function(){ return func.apply(null, args); }, wait); | |
621 | }; | |
622 | ||
623 | // Defers a function, scheduling it to run after the current call stack has | |
624 | // cleared. | |
625 | _.defer = function(func) { | |
626 | return _.delay.apply(_, [func, 1].concat(slice.call(arguments, 1))); | |
627 | }; | |
628 | ||
629 | // Returns a function, that, when invoked, will only be triggered at most once | |
630 | // during a given window of time. | |
631 | _.throttle = function(func, wait) { | |
632 | var context, args, timeout, result; | |
633 | var previous = 0; | |
634 | var later = function() { | |
635 | previous = new Date; | |
636 | timeout = null; | |
637 | result = func.apply(context, args); | |
638 | }; | |
639 | return function() { | |
640 | var now = new Date; | |
641 | var remaining = wait - (now - previous); | |
642 | context = this; | |
643 | args = arguments; | |
644 | if (remaining <= 0) { | |
645 | clearTimeout(timeout); | |
646 | timeout = null; | |
647 | previous = now; | |
648 | result = func.apply(context, args); | |
649 | } else if (!timeout) { | |
650 | timeout = setTimeout(later, remaining); | |
651 | } | |
652 | return result; | |
653 | }; | |
654 | }; | |
655 | ||
656 | // Returns a function, that, as long as it continues to be invoked, will not | |
657 | // be triggered. The function will be called after it stops being called for | |
658 | // N milliseconds. If `immediate` is passed, trigger the function on the | |
659 | // leading edge, instead of the trailing. | |
660 | _.debounce = function(func, wait, immediate) { | |
661 | var timeout, result; | |
662 | return function() { | |
663 | var context = this, args = arguments; | |
664 | var later = function() { | |
665 | timeout = null; | |
666 | if (!immediate) result = func.apply(context, args); | |
667 | }; | |
668 | var callNow = immediate && !timeout; | |
669 | clearTimeout(timeout); | |
670 | timeout = setTimeout(later, wait); | |
671 | if (callNow) result = func.apply(context, args); | |
672 | return result; | |
673 | }; | |
674 | }; | |
675 | ||
676 | // Returns a function that will be executed at most one time, no matter how | |
677 | // often you call it. Useful for lazy initialization. | |
678 | _.once = function(func) { | |
679 | var ran = false, memo; | |
680 | return function() { | |
681 | if (ran) return memo; | |
682 | ran = true; | |
683 | memo = func.apply(this, arguments); | |
684 | func = null; | |
685 | return memo; | |
686 | }; | |
687 | }; | |
688 | ||
689 | // Returns the first function passed as an argument to the second, | |
690 | // allowing you to adjust arguments, run code before and after, and | |
691 | // conditionally execute the original function. | |
692 | _.wrap = function(func, wrapper) { | |
693 | return function() { | |
694 | var args = [func]; | |
695 | push.apply(args, arguments); | |
696 | return wrapper.apply(this, args); | |
697 | }; | |
698 | }; | |
699 | ||
700 | // Returns a function that is the composition of a list of functions, each | |
701 | // consuming the return value of the function that follows. | |
702 | _.compose = function() { | |
703 | var funcs = arguments; | |
704 | return function() { | |
705 | var args = arguments; | |
706 | for (var i = funcs.length - 1; i >= 0; i--) { | |
707 | args = [funcs[i].apply(this, args)]; | |
708 | } | |
709 | return args[0]; | |
710 | }; | |
711 | }; | |
712 | ||
713 | // Returns a function that will only be executed after being called N times. | |
714 | _.after = function(times, func) { | |
715 | if (times <= 0) return func(); | |
716 | return function() { | |
717 | if (--times < 1) { | |
718 | return func.apply(this, arguments); | |
719 | } | |
720 | }; | |
721 | }; | |
722 | ||
723 | // Object Functions | |
724 | // ---------------- | |
725 | ||
726 | // Retrieve the names of an object's properties. | |
727 | // Delegates to **ECMAScript 5**'s native `Object.keys` | |
728 | _.keys = nativeKeys || function(obj) { | |
729 | if (obj !== Object(obj)) throw new TypeError('Invalid object'); | |
730 | var keys = []; | |
731 | for (var key in obj) if (_.has(obj, key)) keys[keys.length] = key; | |
732 | return keys; | |
733 | }; | |
734 | ||
735 | // Retrieve the values of an object's properties. | |
736 | _.values = function(obj) { | |
737 | var values = []; | |
738 | for (var key in obj) if (_.has(obj, key)) values.push(obj[key]); | |
739 | return values; | |
740 | }; | |
741 | ||
742 | // Convert an object into a list of `[key, value]` pairs. | |
743 | _.pairs = function(obj) { | |
744 | var pairs = []; | |
745 | for (var key in obj) if (_.has(obj, key)) pairs.push([key, obj[key]]); | |
746 | return pairs; | |
747 | }; | |
748 | ||
749 | // Invert the keys and values of an object. The values must be serializable. | |
750 | _.invert = function(obj) { | |
751 | var result = {}; | |
752 | for (var key in obj) if (_.has(obj, key)) result[obj[key]] = key; | |
753 | return result; | |
754 | }; | |
755 | ||
756 | // Return a sorted list of the function names available on the object. | |
757 | // Aliased as `methods` | |
758 | _.functions = _.methods = function(obj) { | |
759 | var names = []; | |
760 | for (var key in obj) { | |
761 | if (_.isFunction(obj[key])) names.push(key); | |
762 | } | |
763 | return names.sort(); | |
764 | }; | |
765 | ||
766 | // Extend a given object with all the properties in passed-in object(s). | |
767 | _.extend = function(obj) { | |
768 | each(slice.call(arguments, 1), function(source) { | |
769 | if (source) { | |
770 | for (var prop in source) { | |
771 | obj[prop] = source[prop]; | |
772 | } | |
773 | } | |
774 | }); | |
775 | return obj; | |
776 | }; | |
777 | ||
778 | // Return a copy of the object only containing the whitelisted properties. | |
779 | _.pick = function(obj) { | |
780 | var copy = {}; | |
781 | var keys = concat.apply(ArrayProto, slice.call(arguments, 1)); | |
782 | each(keys, function(key) { | |
783 | if (key in obj) copy[key] = obj[key]; | |
784 | }); | |
785 | return copy; | |
786 | }; | |
787 | ||
788 | // Return a copy of the object without the blacklisted properties. | |
789 | _.omit = function(obj) { | |
790 | var copy = {}; | |
791 | var keys = concat.apply(ArrayProto, slice.call(arguments, 1)); | |
792 | for (var key in obj) { | |
793 | if (!_.contains(keys, key)) copy[key] = obj[key]; | |
794 | } | |
795 | return copy; | |
796 | }; | |
797 | ||
798 | // Fill in a given object with default properties. | |
799 | _.defaults = function(obj) { | |
800 | each(slice.call(arguments, 1), function(source) { | |
801 | if (source) { | |
802 | for (var prop in source) { | |
803 | if (obj[prop] == null) obj[prop] = source[prop]; | |
804 | } | |
805 | } | |
806 | }); | |
807 | return obj; | |
808 | }; | |
809 | ||
810 | // Create a (shallow-cloned) duplicate of an object. | |
811 | _.clone = function(obj) { | |
812 | if (!_.isObject(obj)) return obj; | |
813 | return _.isArray(obj) ? obj.slice() : _.extend({}, obj); | |
814 | }; | |
815 | ||
816 | // Invokes interceptor with the obj, and then returns obj. | |
817 | // The primary purpose of this method is to "tap into" a method chain, in | |
818 | // order to perform operations on intermediate results within the chain. | |
819 | _.tap = function(obj, interceptor) { | |
820 | interceptor(obj); | |
821 | return obj; | |
822 | }; | |
823 | ||
824 | // Internal recursive comparison function for `isEqual`. | |
825 | var eq = function(a, b, aStack, bStack) { | |
826 | // Identical objects are equal. `0 === -0`, but they aren't identical. | |
827 | // See the Harmony `egal` proposal: http://wiki.ecmascript.org/doku.php?id=harmony:egal. | |
828 | if (a === b) return a !== 0 || 1 / a == 1 / b; | |
829 | // A strict comparison is necessary because `null == undefined`. | |
830 | if (a == null || b == null) return a === b; | |
831 | // Unwrap any wrapped objects. | |
832 | if (a instanceof _) a = a._wrapped; | |
833 | if (b instanceof _) b = b._wrapped; | |
834 | // Compare `[[Class]]` names. | |
835 | var className = toString.call(a); | |
836 | if (className != toString.call(b)) return false; | |
837 | switch (className) { | |
838 | // Strings, numbers, dates, and booleans are compared by value. | |
839 | case '[object String]': | |
840 | // Primitives and their corresponding object wrappers are equivalent; thus, `"5"` is | |
841 | // equivalent to `new String("5")`. | |
842 | return a == String(b); | |
843 | case '[object Number]': | |
844 | // `NaN`s are equivalent, but non-reflexive. An `egal` comparison is performed for | |
845 | // other numeric values. | |
846 | return a != +a ? b != +b : (a == 0 ? 1 / a == 1 / b : a == +b); | |
847 | case '[object Date]': | |
848 | case '[object Boolean]': | |
849 | // Coerce dates and booleans to numeric primitive values. Dates are compared by their | |
850 | // millisecond representations. Note that invalid dates with millisecond representations | |
851 | // of `NaN` are not equivalent. | |
852 | return +a == +b; | |
853 | // RegExps are compared by their source patterns and flags. | |
854 | case '[object RegExp]': | |
855 | return a.source == b.source && | |
856 | a.global == b.global && | |
857 | a.multiline == b.multiline && | |
858 | a.ignoreCase == b.ignoreCase; | |
859 | } | |
860 | if (typeof a != 'object' || typeof b != 'object') return false; | |
861 | // Assume equality for cyclic structures. The algorithm for detecting cyclic | |
862 | // structures is adapted from ES 5.1 section 15.12.3, abstract operation `JO`. | |
863 | var length = aStack.length; | |
864 | while (length--) { | |
865 | // Linear search. Performance is inversely proportional to the number of | |
866 | // unique nested structures. | |
867 | if (aStack[length] == a) return bStack[length] == b; | |
868 | } | |
869 | // Add the first object to the stack of traversed objects. | |
870 | aStack.push(a); | |
871 | bStack.push(b); | |
872 | var size = 0, result = true; | |
873 | // Recursively compare objects and arrays. | |
874 | if (className == '[object Array]') { | |
875 | // Compare array lengths to determine if a deep comparison is necessary. | |
876 | size = a.length; | |
877 | result = size == b.length; | |
878 | if (result) { | |
879 | // Deep compare the contents, ignoring non-numeric properties. | |
880 | while (size--) { | |
881 | if (!(result = eq(a[size], b[size], aStack, bStack))) break; | |
882 | } | |
883 | } | |
884 | } else { | |
885 | // Objects with different constructors are not equivalent, but `Object`s | |
886 | // from different frames are. | |
887 | var aCtor = a.constructor, bCtor = b.constructor; | |
888 | if (aCtor !== bCtor && !(_.isFunction(aCtor) && (aCtor instanceof aCtor) && | |
889 | _.isFunction(bCtor) && (bCtor instanceof bCtor))) { | |
890 | return false; | |
891 | } | |
892 | // Deep compare objects. | |
893 | for (var key in a) { | |
894 | if (_.has(a, key)) { | |
895 | // Count the expected number of properties. | |
896 | size++; | |
897 | // Deep compare each member. | |
898 | if (!(result = _.has(b, key) && eq(a[key], b[key], aStack, bStack))) break; | |
899 | } | |
900 | } | |
901 | // Ensure that both objects contain the same number of properties. | |
902 | if (result) { | |
903 | for (key in b) { | |
904 | if (_.has(b, key) && !(size--)) break; | |
905 | } | |
906 | result = !size; | |
907 | } | |
908 | } | |
909 | // Remove the first object from the stack of traversed objects. | |
910 | aStack.pop(); | |
911 | bStack.pop(); | |
912 | return result; | |
913 | }; | |
914 | ||
915 | // Perform a deep comparison to check if two objects are equal. | |
916 | _.isEqual = function(a, b) { | |
917 | return eq(a, b, [], []); | |
918 | }; | |
919 | ||
920 | // Is a given array, string, or object empty? | |
921 | // An "empty" object has no enumerable own-properties. | |
922 | _.isEmpty = function(obj) { | |
923 | if (obj == null) return true; | |
924 | if (_.isArray(obj) || _.isString(obj)) return obj.length === 0; | |
925 | for (var key in obj) if (_.has(obj, key)) return false; | |
926 | return true; | |
927 | }; | |
928 | ||
929 | // Is a given value a DOM element? | |
930 | _.isElement = function(obj) { | |
931 | return !!(obj && obj.nodeType === 1); | |
932 | }; | |
933 | ||
934 | // Is a given value an array? | |
935 | // Delegates to ECMA5's native Array.isArray | |
936 | _.isArray = nativeIsArray || function(obj) { | |
937 | return toString.call(obj) == '[object Array]'; | |
938 | }; | |
939 | ||
940 | // Is a given variable an object? | |
941 | _.isObject = function(obj) { | |
942 | return obj === Object(obj); | |
943 | }; | |
944 | ||
945 | // Add some isType methods: isArguments, isFunction, isString, isNumber, isDate, isRegExp. | |
946 | each(['Arguments', 'Function', 'String', 'Number', 'Date', 'RegExp'], function(name) { | |
947 | _['is' + name] = function(obj) { | |
948 | return toString.call(obj) == '[object ' + name + ']'; | |
949 | }; | |
950 | }); | |
951 | ||
952 | // Define a fallback version of the method in browsers (ahem, IE), where | |
953 | // there isn't any inspectable "Arguments" type. | |
954 | if (!_.isArguments(arguments)) { | |
955 | _.isArguments = function(obj) { | |
956 | return !!(obj && _.has(obj, 'callee')); | |
957 | }; | |
958 | } | |
959 | ||
960 | // Optimize `isFunction` if appropriate. | |
961 | if (typeof (/./) !== 'function') { | |
962 | _.isFunction = function(obj) { | |
963 | return typeof obj === 'function'; | |
964 | }; | |
965 | } | |
966 | ||
967 | // Is a given object a finite number? | |
968 | _.isFinite = function(obj) { | |
969 | return isFinite(obj) && !isNaN(parseFloat(obj)); | |
970 | }; | |
971 | ||
972 | // Is the given value `NaN`? (NaN is the only number which does not equal itself). | |
973 | _.isNaN = function(obj) { | |
974 | return _.isNumber(obj) && obj != +obj; | |
975 | }; | |
976 | ||
977 | // Is a given value a boolean? | |
978 | _.isBoolean = function(obj) { | |
979 | return obj === true || obj === false || toString.call(obj) == '[object Boolean]'; | |
980 | }; | |
981 | ||
982 | // Is a given value equal to null? | |
983 | _.isNull = function(obj) { | |
984 | return obj === null; | |
985 | }; | |
986 | ||
987 | // Is a given variable undefined? | |
988 | _.isUndefined = function(obj) { | |
989 | return obj === void 0; | |
990 | }; | |
991 | ||
992 | // Shortcut function for checking if an object has a given property directly | |
993 | // on itself (in other words, not on a prototype). | |
994 | _.has = function(obj, key) { | |
995 | return hasOwnProperty.call(obj, key); | |
996 | }; | |
997 | ||
998 | // Utility Functions | |
999 | // ----------------- | |
1000 | ||
1001 | // Run Underscore.js in *noConflict* mode, returning the `_` variable to its | |
1002 | // previous owner. Returns a reference to the Underscore object. | |
1003 | _.noConflict = function() { | |
1004 | root._ = previousUnderscore; | |
1005 | return this; | |
1006 | }; | |
1007 | ||
1008 | // Keep the identity function around for default iterators. | |
1009 | _.identity = function(value) { | |
1010 | return value; | |
1011 | }; | |
1012 | ||
1013 | // Run a function **n** times. | |
1014 | _.times = function(n, iterator, context) { | |
1015 | var accum = Array(n); | |
1016 | for (var i = 0; i < n; i++) accum[i] = iterator.call(context, i); | |
1017 | return accum; | |
1018 | }; | |
1019 | ||
1020 | // Return a random integer between min and max (inclusive). | |
1021 | _.random = function(min, max) { | |
1022 | if (max == null) { | |
1023 | max = min; | |
1024 | min = 0; | |
1025 | } | |
1026 | return min + Math.floor(Math.random() * (max - min + 1)); | |
1027 | }; | |
1028 | ||
1029 | // List of HTML entities for escaping. | |
1030 | var entityMap = { | |
1031 | escape: { | |
1032 | '&': '&', | |
1033 | '<': '<', | |
1034 | '>': '>', | |
1035 | '"': '"', | |
1036 | "'": ''', | |
1037 | '/': '/' | |
1038 | } | |
1039 | }; | |
1040 | entityMap.unescape = _.invert(entityMap.escape); | |
1041 | ||
1042 | // Regexes containing the keys and values listed immediately above. | |
1043 | var entityRegexes = { | |
1044 | escape: new RegExp('[' + _.keys(entityMap.escape).join('') + ']', 'g'), | |
1045 | unescape: new RegExp('(' + _.keys(entityMap.unescape).join('|') + ')', 'g') | |
1046 | }; | |
1047 | ||
1048 | // Functions for escaping and unescaping strings to/from HTML interpolation. | |
1049 | _.each(['escape', 'unescape'], function(method) { | |
1050 | _[method] = function(string) { | |
1051 | if (string == null) return ''; | |
1052 | return ('' + string).replace(entityRegexes[method], function(match) { | |
1053 | return entityMap[method][match]; | |
1054 | }); | |
1055 | }; | |
1056 | }); | |
1057 | ||
1058 | // If the value of the named property is a function then invoke it; | |
1059 | // otherwise, return it. | |
1060 | _.result = function(object, property) { | |
1061 | if (object == null) return null; | |
1062 | var value = object[property]; | |
1063 | return _.isFunction(value) ? value.call(object) : value; | |
1064 | }; | |
1065 | ||
1066 | // Add your own custom functions to the Underscore object. | |
1067 | _.mixin = function(obj) { | |
1068 | each(_.functions(obj), function(name){ | |
1069 | var func = _[name] = obj[name]; | |
1070 | _.prototype[name] = function() { | |
1071 | var args = [this._wrapped]; | |
1072 | push.apply(args, arguments); | |
1073 | return result.call(this, func.apply(_, args)); | |
1074 | }; | |
1075 | }); | |
1076 | }; | |
1077 | ||
1078 | // Generate a unique integer id (unique within the entire client session). | |
1079 | // Useful for temporary DOM ids. | |
1080 | var idCounter = 0; | |
1081 | _.uniqueId = function(prefix) { | |
1082 | var id = ++idCounter + ''; | |
1083 | return prefix ? prefix + id : id; | |
1084 | }; | |
1085 | ||
1086 | // By default, Underscore uses ERB-style template delimiters, change the | |
1087 | // following template settings to use alternative delimiters. | |
1088 | _.templateSettings = { | |
1089 | evaluate : /<%([\s\S]+?)%>/g, | |
1090 | interpolate : /<%=([\s\S]+?)%>/g, | |
1091 | escape : /<%-([\s\S]+?)%>/g | |
1092 | }; | |
1093 | ||
1094 | // When customizing `templateSettings`, if you don't want to define an | |
1095 | // interpolation, evaluation or escaping regex, we need one that is | |
1096 | // guaranteed not to match. | |
1097 | var noMatch = /(.)^/; | |
1098 | ||
1099 | // Certain characters need to be escaped so that they can be put into a | |
1100 | // string literal. | |
1101 | var escapes = { | |
1102 | "'": "'", | |
1103 | '\\': '\\', | |
1104 | '\r': 'r', | |
1105 | '\n': 'n', | |
1106 | '\t': 't', | |
1107 | '\u2028': 'u2028', | |
1108 | '\u2029': 'u2029' | |
1109 | }; | |
1110 | ||
1111 | var escaper = /\\|'|\r|\n|\t|\u2028|\u2029/g; | |
1112 | ||
1113 | // JavaScript micro-templating, similar to John Resig's implementation. | |
1114 | // Underscore templating handles arbitrary delimiters, preserves whitespace, | |
1115 | // and correctly escapes quotes within interpolated code. | |
1116 | _.template = function(text, data, settings) { | |
1117 | var render; | |
1118 | settings = _.defaults({}, settings, _.templateSettings); | |
1119 | ||
1120 | // Combine delimiters into one regular expression via alternation. | |
1121 | var matcher = new RegExp([ | |
1122 | (settings.escape || noMatch).source, | |
1123 | (settings.interpolate || noMatch).source, | |
1124 | (settings.evaluate || noMatch).source | |
1125 | ].join('|') + '|$', 'g'); | |
1126 | ||
1127 | // Compile the template source, escaping string literals appropriately. | |
1128 | var index = 0; | |
1129 | var source = "__p+='"; | |
1130 | text.replace(matcher, function(match, escape, interpolate, evaluate, offset) { | |
1131 | source += text.slice(index, offset) | |
1132 | .replace(escaper, function(match) { return '\\' + escapes[match]; }); | |
1133 | ||
1134 | if (escape) { | |
1135 | source += "'+\n((__t=(" + escape + "))==null?'':_.escape(__t))+\n'"; | |
1136 | } | |
1137 | if (interpolate) { | |
1138 | source += "'+\n((__t=(" + interpolate + "))==null?'':__t)+\n'"; | |
1139 | } | |
1140 | if (evaluate) { | |
1141 | source += "';\n" + evaluate + "\n__p+='"; | |
1142 | } | |
1143 | index = offset + match.length; | |
1144 | return match; | |
1145 | }); | |
1146 | source += "';\n"; | |
1147 | ||
1148 | // If a variable is not specified, place data values in local scope. | |
1149 | if (!settings.variable) source = 'with(obj||{}){\n' + source + '}\n'; | |
1150 | ||
1151 | source = "var __t,__p='',__j=Array.prototype.join," + | |
1152 | "print=function(){__p+=__j.call(arguments,'');};\n" + | |
1153 | source + "return __p;\n"; | |
1154 | ||
1155 | try { | |
1156 | render = new Function(settings.variable || 'obj', '_', source); | |
1157 | } catch (e) { | |
1158 | e.source = source; | |
1159 | throw e; | |
1160 | } | |
1161 | ||
1162 | if (data) return render(data, _); | |
1163 | var template = function(data) { | |
1164 | return render.call(this, data, _); | |
1165 | }; | |
1166 | ||
1167 | // Provide the compiled function source as a convenience for precompilation. | |
1168 | template.source = 'function(' + (settings.variable || 'obj') + '){\n' + source + '}'; | |
1169 | ||
1170 | return template; | |
1171 | }; | |
1172 | ||
1173 | // Add a "chain" function, which will delegate to the wrapper. | |
1174 | _.chain = function(obj) { | |
1175 | return _(obj).chain(); | |
1176 | }; | |
1177 | ||
1178 | // OOP | |
1179 | // --------------- | |
1180 | // If Underscore is called as a function, it returns a wrapped object that | |
1181 | // can be used OO-style. This wrapper holds altered versions of all the | |
1182 | // underscore functions. Wrapped objects may be chained. | |
1183 | ||
1184 | // Helper function to continue chaining intermediate results. | |
1185 | var result = function(obj) { | |
1186 | return this._chain ? _(obj).chain() : obj; | |
1187 | }; | |
1188 | ||
1189 | // Add all of the Underscore functions to the wrapper object. | |
1190 | _.mixin(_); | |
1191 | ||
1192 | // Add all mutator Array functions to the wrapper. | |
1193 | each(['pop', 'push', 'reverse', 'shift', 'sort', 'splice', 'unshift'], function(name) { | |
1194 | var method = ArrayProto[name]; | |
1195 | _.prototype[name] = function() { | |
1196 | var obj = this._wrapped; | |
1197 | method.apply(obj, arguments); | |
1198 | if ((name == 'shift' || name == 'splice') && obj.length === 0) delete obj[0]; | |
1199 | return result.call(this, obj); | |
1200 | }; | |
1201 | }); | |
1202 | ||
1203 | // Add all accessor Array functions to the wrapper. | |
1204 | each(['concat', 'join', 'slice'], function(name) { | |
1205 | var method = ArrayProto[name]; | |
1206 | _.prototype[name] = function() { | |
1207 | return result.call(this, method.apply(this._wrapped, arguments)); | |
1208 | }; | |
1209 | }); | |
1210 | ||
1211 | _.extend(_.prototype, { | |
1212 | ||
1213 | // Start chaining a wrapped Underscore object. | |
1214 | chain: function() { | |
1215 | this._chain = true; | |
1216 | return this; | |
1217 | }, | |
1218 | ||
1219 | // Extracts the result from a wrapped and chained object. | |
1220 | value: function() { | |
1221 | return this._wrapped; | |
1222 | } | |
1223 | ||
1224 | }); | |
1225 | ||
1226 | }).call(this); |