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