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