judge 1.1.0 → 1.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,5 +1,5 @@
1
- // Underscore.js 1.1.6
2
- // (c) 2011 Jeremy Ashkenas, DocumentCloud Inc.
1
+ // Underscore.js 1.3.1
2
+ // (c) 2009-2012 Jeremy Ashkenas, DocumentCloud Inc.
3
3
  // Underscore is freely distributable under the MIT license.
4
4
  // Portions of Underscore are inspired or borrowed from Prototype,
5
5
  // Oliver Steele's Functional, and John Resig's Micro-Templating.
@@ -48,37 +48,39 @@
48
48
  // Create a safe reference to the Underscore object for use below.
49
49
  var _ = function(obj) { return new wrapper(obj); };
50
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
- _._ = _;
51
+ // Export the Underscore object for **Node.js**, with
52
+ // backwards-compatibility for the old `require()` API. If we're in
53
+ // the browser, add `_` as a global object via a string identifier,
54
+ // for Closure Compiler "advanced" mode.
55
+ if (typeof exports !== 'undefined') {
56
+ if (typeof module !== 'undefined' && module.exports) {
57
+ exports = module.exports = _;
58
+ }
59
+ exports._ = _;
57
60
  } else {
58
- // Exported as a string, for Closure Compiler "advanced" mode.
59
61
  root['_'] = _;
60
62
  }
61
63
 
62
64
  // Current version.
63
- _.VERSION = '1.1.6';
65
+ _.VERSION = '1.3.1';
64
66
 
65
67
  // Collection Functions
66
68
  // --------------------
67
69
 
68
70
  // The cornerstone, an `each` implementation, aka `forEach`.
69
- // Handles objects implementing `forEach`, arrays, and raw objects.
71
+ // Handles objects with the built-in `forEach`, arrays, and raw objects.
70
72
  // Delegates to **ECMAScript 5**'s native `forEach` if available.
71
73
  var each = _.each = _.forEach = function(obj, iterator, context) {
72
74
  if (obj == null) return;
73
75
  if (nativeForEach && obj.forEach === nativeForEach) {
74
76
  obj.forEach(iterator, context);
75
- } else if (_.isNumber(obj.length)) {
77
+ } else if (obj.length === +obj.length) {
76
78
  for (var i = 0, l = obj.length; i < l; i++) {
77
79
  if (i in obj && iterator.call(context, obj[i], i, obj) === breaker) return;
78
80
  }
79
81
  } else {
80
82
  for (var key in obj) {
81
- if (hasOwnProperty.call(obj, key)) {
83
+ if (_.has(obj, key)) {
82
84
  if (iterator.call(context, obj[key], key, obj) === breaker) return;
83
85
  }
84
86
  }
@@ -87,47 +89,50 @@
87
89
 
88
90
  // Return the results of applying the iterator to each element.
89
91
  // Delegates to **ECMAScript 5**'s native `map` if available.
90
- _.map = function(obj, iterator, context) {
92
+ _.map = _.collect = function(obj, iterator, context) {
91
93
  var results = [];
92
94
  if (obj == null) return results;
93
95
  if (nativeMap && obj.map === nativeMap) return obj.map(iterator, context);
94
96
  each(obj, function(value, index, list) {
95
97
  results[results.length] = iterator.call(context, value, index, list);
96
98
  });
99
+ if (obj.length === +obj.length) results.length = obj.length;
97
100
  return results;
98
101
  };
99
102
 
100
103
  // **Reduce** builds up a single result from a list of values, aka `inject`,
101
104
  // or `foldl`. Delegates to **ECMAScript 5**'s native `reduce` if available.
102
105
  _.reduce = _.foldl = _.inject = function(obj, iterator, memo, context) {
103
- var initial = memo !== void 0;
106
+ var initial = arguments.length > 2;
104
107
  if (obj == null) obj = [];
105
108
  if (nativeReduce && obj.reduce === nativeReduce) {
106
109
  if (context) iterator = _.bind(iterator, context);
107
110
  return initial ? obj.reduce(iterator, memo) : obj.reduce(iterator);
108
111
  }
109
112
  each(obj, function(value, index, list) {
110
- if (!initial && index === 0) {
113
+ if (!initial) {
111
114
  memo = value;
112
115
  initial = true;
113
116
  } else {
114
117
  memo = iterator.call(context, memo, value, index, list);
115
118
  }
116
119
  });
117
- if (!initial) throw new TypeError("Reduce of empty array with no initial value");
120
+ if (!initial) throw new TypeError('Reduce of empty array with no initial value');
118
121
  return memo;
119
122
  };
120
123
 
121
124
  // The right-associative version of reduce, also known as `foldr`.
122
125
  // Delegates to **ECMAScript 5**'s native `reduceRight` if available.
123
126
  _.reduceRight = _.foldr = function(obj, iterator, memo, context) {
127
+ var initial = arguments.length > 2;
124
128
  if (obj == null) obj = [];
125
129
  if (nativeReduceRight && obj.reduceRight === nativeReduceRight) {
126
130
  if (context) iterator = _.bind(iterator, context);
127
- return memo !== void 0 ? obj.reduceRight(iterator, memo) : obj.reduceRight(iterator);
131
+ return initial ? obj.reduceRight(iterator, memo) : obj.reduceRight(iterator);
128
132
  }
129
- var reversed = (_.isArray(obj) ? obj.slice() : _.toArray(obj)).reverse();
130
- return _.reduce(reversed, iterator, memo, context);
133
+ var reversed = _.toArray(obj).reverse();
134
+ if (context && !initial) iterator = _.bind(iterator, context);
135
+ return initial ? _.reduce(reversed, iterator, memo, context) : _.reduce(reversed, iterator);
131
136
  };
132
137
 
133
138
  // Return the first value which passes a truth test. Aliased as `detect`.
@@ -187,9 +192,9 @@
187
192
  if (obj == null) return result;
188
193
  if (nativeSome && obj.some === nativeSome) return obj.some(iterator, context);
189
194
  each(obj, function(value, index, list) {
190
- if (result = iterator.call(context, value, index, list)) return breaker;
195
+ if (result || (result = iterator.call(context, value, index, list))) return breaker;
191
196
  });
192
- return result;
197
+ return !!result;
193
198
  };
194
199
 
195
200
  // Determine if a given value is included in the array or object using `===`.
@@ -198,8 +203,8 @@
198
203
  var found = false;
199
204
  if (obj == null) return found;
200
205
  if (nativeIndexOf && obj.indexOf === nativeIndexOf) return obj.indexOf(target) != -1;
201
- any(obj, function(value) {
202
- if (found = value === target) return true;
206
+ found = any(obj, function(value) {
207
+ return value === target;
203
208
  });
204
209
  return found;
205
210
  };
@@ -208,7 +213,7 @@
208
213
  _.invoke = function(obj, method) {
209
214
  var args = slice.call(arguments, 2);
210
215
  return _.map(obj, function(value) {
211
- return (method.call ? method || value : value[method]).apply(value, args);
216
+ return (_.isFunction(method) ? method || value : value[method]).apply(value, args);
212
217
  });
213
218
  };
214
219
 
@@ -220,6 +225,7 @@
220
225
  // Return the maximum element or (element-based computation).
221
226
  _.max = function(obj, iterator, context) {
222
227
  if (!iterator && _.isArray(obj)) return Math.max.apply(Math, obj);
228
+ if (!iterator && _.isEmpty(obj)) return -Infinity;
223
229
  var result = {computed : -Infinity};
224
230
  each(obj, function(value, index, list) {
225
231
  var computed = iterator ? iterator.call(context, value, index, list) : value;
@@ -231,6 +237,7 @@
231
237
  // Return the minimum element (or element-based computation).
232
238
  _.min = function(obj, iterator, context) {
233
239
  if (!iterator && _.isArray(obj)) return Math.min.apply(Math, obj);
240
+ if (!iterator && _.isEmpty(obj)) return Infinity;
234
241
  var result = {computed : Infinity};
235
242
  each(obj, function(value, index, list) {
236
243
  var computed = iterator ? iterator.call(context, value, index, list) : value;
@@ -239,6 +246,21 @@
239
246
  return result.value;
240
247
  };
241
248
 
249
+ // Shuffle an array.
250
+ _.shuffle = function(obj) {
251
+ var shuffled = [], rand;
252
+ each(obj, function(value, index, list) {
253
+ if (index == 0) {
254
+ shuffled[0] = value;
255
+ } else {
256
+ rand = Math.floor(Math.random() * (index + 1));
257
+ shuffled[index] = shuffled[rand];
258
+ shuffled[rand] = value;
259
+ }
260
+ });
261
+ return shuffled;
262
+ };
263
+
242
264
  // Sort the object's values by a criterion produced by an iterator.
243
265
  _.sortBy = function(obj, iterator, context) {
244
266
  return _.pluck(_.map(obj, function(value, index, list) {
@@ -252,9 +274,11 @@
252
274
  }), 'value');
253
275
  };
254
276
 
255
- // Groups the object's values by a criterion produced by an iterator
256
- _.groupBy = function(obj, iterator) {
277
+ // Groups the object's values by a criterion. Pass either a string attribute
278
+ // to group by, or a function that returns the criterion.
279
+ _.groupBy = function(obj, val) {
257
280
  var result = {};
281
+ var iterator = _.isFunction(val) ? val : function(obj) { return obj[val]; };
258
282
  each(obj, function(value, index) {
259
283
  var key = iterator(value, index);
260
284
  (result[key] || (result[key] = [])).push(value);
@@ -278,7 +302,7 @@
278
302
  _.toArray = function(iterable) {
279
303
  if (!iterable) return [];
280
304
  if (iterable.toArray) return iterable.toArray();
281
- if (_.isArray(iterable)) return iterable;
305
+ if (_.isArray(iterable)) return slice.call(iterable);
282
306
  if (_.isArguments(iterable)) return slice.call(iterable);
283
307
  return _.values(iterable);
284
308
  };
@@ -298,6 +322,24 @@
298
322
  return (n != null) && !guard ? slice.call(array, 0, n) : array[0];
299
323
  };
300
324
 
325
+ // Returns everything but the last entry of the array. Especcialy useful on
326
+ // the arguments object. Passing **n** will return all the values in
327
+ // the array, excluding the last N. The **guard** check allows it to work with
328
+ // `_.map`.
329
+ _.initial = function(array, n, guard) {
330
+ return slice.call(array, 0, array.length - ((n == null) || guard ? 1 : n));
331
+ };
332
+
333
+ // Get the last element of an array. Passing **n** will return the last N
334
+ // values in the array. The **guard** check allows it to work with `_.map`.
335
+ _.last = function(array, n, guard) {
336
+ if ((n != null) && !guard) {
337
+ return slice.call(array, Math.max(array.length - n, 0));
338
+ } else {
339
+ return array[array.length - 1];
340
+ }
341
+ };
342
+
301
343
  // Returns everything but the first entry of the array. Aliased as `tail`.
302
344
  // Especially useful on the arguments object. Passing an **index** will return
303
345
  // the rest of the values in the array from that index onward. The **guard**
@@ -306,20 +348,15 @@
306
348
  return slice.call(array, (index == null) || guard ? 1 : index);
307
349
  };
308
350
 
309
- // Get the last element of an array.
310
- _.last = function(array) {
311
- return array[array.length - 1];
312
- };
313
-
314
351
  // Trim out all falsy values from an array.
315
352
  _.compact = function(array) {
316
353
  return _.filter(array, function(value){ return !!value; });
317
354
  };
318
355
 
319
356
  // Return a completely flattened version of an array.
320
- _.flatten = function(array) {
357
+ _.flatten = function(array, shallow) {
321
358
  return _.reduce(array, function(memo, value) {
322
- if (_.isArray(value)) return memo.concat(_.flatten(value));
359
+ if (_.isArray(value)) return memo.concat(shallow ? value : _.flatten(value));
323
360
  memo[memo.length] = value;
324
361
  return memo;
325
362
  }, []);
@@ -327,23 +364,34 @@
327
364
 
328
365
  // Return a version of the array that does not contain the specified value(s).
329
366
  _.without = function(array) {
330
- var values = slice.call(arguments, 1);
331
- return _.filter(array, function(value){ return !_.include(values, value); });
367
+ return _.difference(array, slice.call(arguments, 1));
332
368
  };
333
369
 
334
370
  // Produce a duplicate-free version of the array. If the array has already
335
371
  // been sorted, you have the option of using a faster algorithm.
336
372
  // Aliased as `unique`.
337
- _.uniq = _.unique = function(array, isSorted) {
338
- return _.reduce(array, function(memo, el, i) {
339
- if (0 == i || (isSorted === true ? _.last(memo) != el : !_.include(memo, el))) memo[memo.length] = el;
373
+ _.uniq = _.unique = function(array, isSorted, iterator) {
374
+ var initial = iterator ? _.map(array, iterator) : array;
375
+ var result = [];
376
+ _.reduce(initial, function(memo, el, i) {
377
+ if (0 == i || (isSorted === true ? _.last(memo) != el : !_.include(memo, el))) {
378
+ memo[memo.length] = el;
379
+ result[result.length] = array[i];
380
+ }
340
381
  return memo;
341
382
  }, []);
383
+ return result;
384
+ };
385
+
386
+ // Produce an array that contains the union: each distinct element from all of
387
+ // the passed-in arrays.
388
+ _.union = function() {
389
+ return _.uniq(_.flatten(arguments, true));
342
390
  };
343
391
 
344
392
  // Produce an array that contains every item shared between all the
345
- // passed-in arrays.
346
- _.intersect = function(array) {
393
+ // passed-in arrays. (Aliased as "intersect" for back-compat.)
394
+ _.intersection = _.intersect = function(array) {
347
395
  var rest = slice.call(arguments, 1);
348
396
  return _.filter(_.uniq(array), function(item) {
349
397
  return _.every(rest, function(other) {
@@ -352,6 +400,13 @@
352
400
  });
353
401
  };
354
402
 
403
+ // Take the difference between one array and a number of other arrays.
404
+ // Only the elements present in just the first array will remain.
405
+ _.difference = function(array) {
406
+ var rest = _.flatten(slice.call(arguments, 1));
407
+ return _.filter(array, function(value){ return !_.include(rest, value); });
408
+ };
409
+
355
410
  // Zip together multiple lists into a single array -- elements that share
356
411
  // an index go together.
357
412
  _.zip = function() {
@@ -376,17 +431,16 @@
376
431
  return array[i] === item ? i : -1;
377
432
  }
378
433
  if (nativeIndexOf && array.indexOf === nativeIndexOf) return array.indexOf(item);
379
- for (i = 0, l = array.length; i < l; i++) if (array[i] === item) return i;
434
+ for (i = 0, l = array.length; i < l; i++) if (i in array && array[i] === item) return i;
380
435
  return -1;
381
436
  };
382
437
 
383
-
384
438
  // Delegates to **ECMAScript 5**'s native `lastIndexOf` if available.
385
439
  _.lastIndexOf = function(array, item) {
386
440
  if (array == null) return -1;
387
441
  if (nativeLastIndexOf && array.lastIndexOf === nativeLastIndexOf) return array.lastIndexOf(item);
388
442
  var i = array.length;
389
- while (i--) if (array[i] === item) return i;
443
+ while (i--) if (i in array && array[i] === item) return i;
390
444
  return -1;
391
445
  };
392
446
 
@@ -415,15 +469,25 @@
415
469
  // Function (ahem) Functions
416
470
  // ------------------
417
471
 
472
+ // Reusable constructor function for prototype setting.
473
+ var ctor = function(){};
474
+
418
475
  // Create a function bound to a given object (assigning `this`, and arguments,
419
476
  // optionally). Binding with arguments is also known as `curry`.
420
477
  // Delegates to **ECMAScript 5**'s native `Function.bind` if available.
421
478
  // We check for `func.bind` first, to fail fast when `func` is undefined.
422
- _.bind = function(func, obj) {
479
+ _.bind = function bind(func, context) {
480
+ var bound, args;
423
481
  if (func.bind === nativeBind && nativeBind) return nativeBind.apply(func, slice.call(arguments, 1));
424
- var args = slice.call(arguments, 2);
425
- return function() {
426
- return func.apply(obj, args.concat(slice.call(arguments)));
482
+ if (!_.isFunction(func)) throw new TypeError;
483
+ args = slice.call(arguments, 2);
484
+ return bound = function() {
485
+ if (!(this instanceof bound)) return func.apply(context, args.concat(slice.call(arguments)));
486
+ ctor.prototype = func.prototype;
487
+ var self = new ctor;
488
+ var result = func.apply(self, args.concat(slice.call(arguments)));
489
+ if (Object(result) === result) return result;
490
+ return self;
427
491
  };
428
492
  };
429
493
 
@@ -442,7 +506,7 @@
442
506
  hasher || (hasher = _.identity);
443
507
  return function() {
444
508
  var key = hasher.apply(this, arguments);
445
- return hasOwnProperty.call(memo, key) ? memo[key] : (memo[key] = func.apply(this, arguments));
509
+ return _.has(memo, key) ? memo[key] : (memo[key] = func.apply(this, arguments));
446
510
  };
447
511
  };
448
512
 
@@ -459,31 +523,43 @@
459
523
  return _.delay.apply(_, [func, 1].concat(slice.call(arguments, 1)));
460
524
  };
461
525
 
462
- // Internal function used to implement `_.throttle` and `_.debounce`.
463
- var limit = function(func, wait, debounce) {
464
- var timeout;
526
+ // Returns a function, that, when invoked, will only be triggered at most once
527
+ // during a given window of time.
528
+ _.throttle = function(func, wait) {
529
+ var context, args, timeout, throttling, more;
530
+ var whenDone = _.debounce(function(){ more = throttling = false; }, wait);
465
531
  return function() {
466
- var context = this, args = arguments;
467
- var throttler = function() {
532
+ context = this; args = arguments;
533
+ var later = function() {
468
534
  timeout = null;
469
- func.apply(context, args);
535
+ if (more) func.apply(context, args);
536
+ whenDone();
470
537
  };
471
- if (debounce) clearTimeout(timeout);
472
- if (debounce || !timeout) timeout = setTimeout(throttler, wait);
538
+ if (!timeout) timeout = setTimeout(later, wait);
539
+ if (throttling) {
540
+ more = true;
541
+ } else {
542
+ func.apply(context, args);
543
+ }
544
+ whenDone();
545
+ throttling = true;
473
546
  };
474
547
  };
475
548
 
476
- // Returns a function, that, when invoked, will only be triggered at most once
477
- // during a given window of time.
478
- _.throttle = function(func, wait) {
479
- return limit(func, wait, false);
480
- };
481
-
482
549
  // Returns a function, that, as long as it continues to be invoked, will not
483
550
  // be triggered. The function will be called after it stops being called for
484
551
  // N milliseconds.
485
552
  _.debounce = function(func, wait) {
486
- return limit(func, wait, true);
553
+ var timeout;
554
+ return function() {
555
+ var context = this, args = arguments;
556
+ var later = function() {
557
+ timeout = null;
558
+ func.apply(context, args);
559
+ };
560
+ clearTimeout(timeout);
561
+ timeout = setTimeout(later, wait);
562
+ };
487
563
  };
488
564
 
489
565
  // Returns a function that will be executed at most one time, no matter how
@@ -502,7 +578,7 @@
502
578
  // conditionally execute the original function.
503
579
  _.wrap = function(func, wrapper) {
504
580
  return function() {
505
- var args = [func].concat(slice.call(arguments));
581
+ var args = [func].concat(slice.call(arguments, 0));
506
582
  return wrapper.apply(this, args);
507
583
  };
508
584
  };
@@ -510,9 +586,9 @@
510
586
  // Returns a function that is the composition of a list of functions, each
511
587
  // consuming the return value of the function that follows.
512
588
  _.compose = function() {
513
- var funcs = slice.call(arguments);
589
+ var funcs = arguments;
514
590
  return function() {
515
- var args = slice.call(arguments);
591
+ var args = arguments;
516
592
  for (var i = funcs.length - 1; i >= 0; i--) {
517
593
  args = [funcs[i].apply(this, args)];
518
594
  }
@@ -522,12 +598,12 @@
522
598
 
523
599
  // Returns a function that will only be executed after being called N times.
524
600
  _.after = function(times, func) {
601
+ if (times <= 0) return func();
525
602
  return function() {
526
603
  if (--times < 1) { return func.apply(this, arguments); }
527
604
  };
528
605
  };
529
606
 
530
-
531
607
  // Object Functions
532
608
  // ----------------
533
609
 
@@ -536,7 +612,7 @@
536
612
  _.keys = nativeKeys || function(obj) {
537
613
  if (obj !== Object(obj)) throw new TypeError('Invalid object');
538
614
  var keys = [];
539
- for (var key in obj) if (hasOwnProperty.call(obj, key)) keys[keys.length] = key;
615
+ for (var key in obj) if (_.has(obj, key)) keys[keys.length] = key;
540
616
  return keys;
541
617
  };
542
618
 
@@ -548,14 +624,18 @@
548
624
  // Return a sorted list of the function names available on the object.
549
625
  // Aliased as `methods`
550
626
  _.functions = _.methods = function(obj) {
551
- return _.filter(_.keys(obj), function(key){ return _.isFunction(obj[key]); }).sort();
627
+ var names = [];
628
+ for (var key in obj) {
629
+ if (_.isFunction(obj[key])) names.push(key);
630
+ }
631
+ return names.sort();
552
632
  };
553
633
 
554
634
  // Extend a given object with all the properties in passed-in object(s).
555
635
  _.extend = function(obj) {
556
636
  each(slice.call(arguments, 1), function(source) {
557
637
  for (var prop in source) {
558
- if (source[prop] !== void 0) obj[prop] = source[prop];
638
+ obj[prop] = source[prop];
559
639
  }
560
640
  });
561
641
  return obj;
@@ -573,6 +653,7 @@
573
653
 
574
654
  // Create a (shallow-cloned) duplicate of an object.
575
655
  _.clone = function(obj) {
656
+ if (!_.isObject(obj)) return obj;
576
657
  return _.isArray(obj) ? obj.slice() : _.extend({}, obj);
577
658
  };
578
659
 
@@ -584,50 +665,104 @@
584
665
  return obj;
585
666
  };
586
667
 
587
- // Perform a deep comparison to check if two objects are equal.
588
- _.isEqual = function(a, b) {
589
- // Check object identity.
590
- if (a === b) return true;
591
- // Different types?
592
- var atype = typeof(a), btype = typeof(b);
593
- if (atype != btype) return false;
594
- // Basic equality test (watch out for coercions).
595
- if (a == b) return true;
596
- // One is falsy and the other truthy.
597
- if ((!a && b) || (a && !b)) return false;
668
+ // Internal recursive comparison function.
669
+ function eq(a, b, stack) {
670
+ // Identical objects are equal. `0 === -0`, but they aren't identical.
671
+ // See the Harmony `egal` proposal: http://wiki.ecmascript.org/doku.php?id=harmony:egal.
672
+ if (a === b) return a !== 0 || 1 / a == 1 / b;
673
+ // A strict comparison is necessary because `null == undefined`.
674
+ if (a == null || b == null) return a === b;
598
675
  // Unwrap any wrapped objects.
599
676
  if (a._chain) a = a._wrapped;
600
677
  if (b._chain) b = b._wrapped;
601
- // One of them implements an isEqual()?
602
- if (a.isEqual) return a.isEqual(b);
603
- if (b.isEqual) return b.isEqual(a);
604
- // Check dates' integer values.
605
- if (_.isDate(a) && _.isDate(b)) return a.getTime() === b.getTime();
606
- // Both are NaN?
607
- if (_.isNaN(a) && _.isNaN(b)) return false;
608
- // Compare regular expressions.
609
- if (_.isRegExp(a) && _.isRegExp(b))
610
- return a.source === b.source &&
611
- a.global === b.global &&
612
- a.ignoreCase === b.ignoreCase &&
613
- a.multiline === b.multiline;
614
- // If a is not an object by this point, we can't handle it.
615
- if (atype !== 'object') return false;
616
- // Check for different array lengths before comparing contents.
617
- if (a.length && (a.length !== b.length)) return false;
618
- // Nothing else worked, deep compare the contents.
619
- var aKeys = _.keys(a), bKeys = _.keys(b);
620
- // Different object sizes?
621
- if (aKeys.length != bKeys.length) return false;
622
- // Recursive comparison of contents.
623
- for (var key in a) if (!(key in b) || !_.isEqual(a[key], b[key])) return false;
624
- return true;
678
+ // Invoke a custom `isEqual` method if one is provided.
679
+ if (a.isEqual && _.isFunction(a.isEqual)) return a.isEqual(b);
680
+ if (b.isEqual && _.isFunction(b.isEqual)) return b.isEqual(a);
681
+ // Compare `[[Class]]` names.
682
+ var className = toString.call(a);
683
+ if (className != toString.call(b)) return false;
684
+ switch (className) {
685
+ // Strings, numbers, dates, and booleans are compared by value.
686
+ case '[object String]':
687
+ // Primitives and their corresponding object wrappers are equivalent; thus, `"5"` is
688
+ // equivalent to `new String("5")`.
689
+ return a == String(b);
690
+ case '[object Number]':
691
+ // `NaN`s are equivalent, but non-reflexive. An `egal` comparison is performed for
692
+ // other numeric values.
693
+ return a != +a ? b != +b : (a == 0 ? 1 / a == 1 / b : a == +b);
694
+ case '[object Date]':
695
+ case '[object Boolean]':
696
+ // Coerce dates and booleans to numeric primitive values. Dates are compared by their
697
+ // millisecond representations. Note that invalid dates with millisecond representations
698
+ // of `NaN` are not equivalent.
699
+ return +a == +b;
700
+ // RegExps are compared by their source patterns and flags.
701
+ case '[object RegExp]':
702
+ return a.source == b.source &&
703
+ a.global == b.global &&
704
+ a.multiline == b.multiline &&
705
+ a.ignoreCase == b.ignoreCase;
706
+ }
707
+ if (typeof a != 'object' || typeof b != 'object') return false;
708
+ // Assume equality for cyclic structures. The algorithm for detecting cyclic
709
+ // structures is adapted from ES 5.1 section 15.12.3, abstract operation `JO`.
710
+ var length = stack.length;
711
+ while (length--) {
712
+ // Linear search. Performance is inversely proportional to the number of
713
+ // unique nested structures.
714
+ if (stack[length] == a) return true;
715
+ }
716
+ // Add the first object to the stack of traversed objects.
717
+ stack.push(a);
718
+ var size = 0, result = true;
719
+ // Recursively compare objects and arrays.
720
+ if (className == '[object Array]') {
721
+ // Compare array lengths to determine if a deep comparison is necessary.
722
+ size = a.length;
723
+ result = size == b.length;
724
+ if (result) {
725
+ // Deep compare the contents, ignoring non-numeric properties.
726
+ while (size--) {
727
+ // Ensure commutative equality for sparse arrays.
728
+ if (!(result = size in a == size in b && eq(a[size], b[size], stack))) break;
729
+ }
730
+ }
731
+ } else {
732
+ // Objects with different constructors are not equivalent.
733
+ if ('constructor' in a != 'constructor' in b || a.constructor != b.constructor) return false;
734
+ // Deep compare objects.
735
+ for (var key in a) {
736
+ if (_.has(a, key)) {
737
+ // Count the expected number of properties.
738
+ size++;
739
+ // Deep compare each member.
740
+ if (!(result = _.has(b, key) && eq(a[key], b[key], stack))) break;
741
+ }
742
+ }
743
+ // Ensure that both objects contain the same number of properties.
744
+ if (result) {
745
+ for (key in b) {
746
+ if (_.has(b, key) && !(size--)) break;
747
+ }
748
+ result = !size;
749
+ }
750
+ }
751
+ // Remove the first object from the stack of traversed objects.
752
+ stack.pop();
753
+ return result;
754
+ }
755
+
756
+ // Perform a deep comparison to check if two objects are equal.
757
+ _.isEqual = function(a, b) {
758
+ return eq(a, b, []);
625
759
  };
626
760
 
627
- // Is a given array or object empty?
761
+ // Is a given array, string, or object empty?
762
+ // An "empty" object has no enumerable own-properties.
628
763
  _.isEmpty = function(obj) {
629
764
  if (_.isArray(obj) || _.isString(obj)) return obj.length === 0;
630
- for (var key in obj) if (hasOwnProperty.call(obj, key)) return false;
765
+ for (var key in obj) if (_.has(obj, key)) return false;
631
766
  return true;
632
767
  };
633
768
 
@@ -639,7 +774,7 @@
639
774
  // Is a given value an array?
640
775
  // Delegates to ECMA5's native Array.isArray
641
776
  _.isArray = nativeIsArray || function(obj) {
642
- return toString.call(obj) === '[object Array]';
777
+ return toString.call(obj) == '[object Array]';
643
778
  };
644
779
 
645
780
  // Is a given variable an object?
@@ -649,43 +784,48 @@
649
784
 
650
785
  // Is a given variable an arguments object?
651
786
  _.isArguments = function(obj) {
652
- return !!(obj && hasOwnProperty.call(obj, 'callee'));
787
+ return toString.call(obj) == '[object Arguments]';
653
788
  };
789
+ if (!_.isArguments(arguments)) {
790
+ _.isArguments = function(obj) {
791
+ return !!(obj && _.has(obj, 'callee'));
792
+ };
793
+ }
654
794
 
655
795
  // Is a given value a function?
656
796
  _.isFunction = function(obj) {
657
- return !!(obj && obj.constructor && obj.call && obj.apply);
797
+ return toString.call(obj) == '[object Function]';
658
798
  };
659
799
 
660
800
  // Is a given value a string?
661
801
  _.isString = function(obj) {
662
- return !!(obj === '' || (obj && obj.charCodeAt && obj.substr));
802
+ return toString.call(obj) == '[object String]';
663
803
  };
664
804
 
665
805
  // Is a given value a number?
666
806
  _.isNumber = function(obj) {
667
- return !!(obj === 0 || (obj && obj.toExponential && obj.toFixed));
807
+ return toString.call(obj) == '[object Number]';
668
808
  };
669
809
 
670
- // Is the given value `NaN`? `NaN` happens to be the only value in JavaScript
671
- // that does not equal itself.
810
+ // Is the given value `NaN`?
672
811
  _.isNaN = function(obj) {
812
+ // `NaN` is the only value for which `===` is not reflexive.
673
813
  return obj !== obj;
674
814
  };
675
815
 
676
816
  // Is a given value a boolean?
677
817
  _.isBoolean = function(obj) {
678
- return obj === true || obj === false;
818
+ return obj === true || obj === false || toString.call(obj) == '[object Boolean]';
679
819
  };
680
820
 
681
821
  // Is a given value a date?
682
822
  _.isDate = function(obj) {
683
- return !!(obj && obj.getTimezoneOffset && obj.setUTCFullYear);
823
+ return toString.call(obj) == '[object Date]';
684
824
  };
685
825
 
686
826
  // Is the given value a regular expression?
687
827
  _.isRegExp = function(obj) {
688
- return !!(obj && obj.test && obj.exec && (obj.ignoreCase || obj.ignoreCase === false));
828
+ return toString.call(obj) == '[object RegExp]';
689
829
  };
690
830
 
691
831
  // Is a given value equal to null?
@@ -698,6 +838,11 @@
698
838
  return obj === void 0;
699
839
  };
700
840
 
841
+ // Has own property?
842
+ _.has = function(obj, key) {
843
+ return hasOwnProperty.call(obj, key);
844
+ };
845
+
701
846
  // Utility Functions
702
847
  // -----------------
703
848
 
@@ -718,6 +863,11 @@
718
863
  for (var i = 0; i < n; i++) iterator.call(context, i);
719
864
  };
720
865
 
866
+ // Escape a string for HTML interpolation.
867
+ _.escape = function(string) {
868
+ return (''+string).replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;').replace(/"/g, '&quot;').replace(/'/g, '&#x27;').replace(/\//g,'&#x2F;');
869
+ };
870
+
721
871
  // Add your own custom functions to the Underscore object, ensuring that
722
872
  // they're correctly added to the OOP wrapper as well.
723
873
  _.mixin = function(obj) {
@@ -738,7 +888,19 @@
738
888
  // following template settings to use alternative delimiters.
739
889
  _.templateSettings = {
740
890
  evaluate : /<%([\s\S]+?)%>/g,
741
- interpolate : /<%=([\s\S]+?)%>/g
891
+ interpolate : /<%=([\s\S]+?)%>/g,
892
+ escape : /<%-([\s\S]+?)%>/g
893
+ };
894
+
895
+ // When customizing `templateSettings`, if you don't want to define an
896
+ // interpolation, evaluation or escaping regex, we need one that is
897
+ // guaranteed not to match.
898
+ var noMatch = /.^/;
899
+
900
+ // Within an interpolation, evaluation, or escaping, remove HTML escaping
901
+ // that had been previously added.
902
+ var unescape = function(code) {
903
+ return code.replace(/\\\\/g, '\\').replace(/\\'/g, "'");
742
904
  };
743
905
 
744
906
  // JavaScript micro-templating, similar to John Resig's implementation.
@@ -750,19 +912,29 @@
750
912
  'with(obj||{}){__p.push(\'' +
751
913
  str.replace(/\\/g, '\\\\')
752
914
  .replace(/'/g, "\\'")
753
- .replace(c.interpolate, function(match, code) {
754
- return "'," + code.replace(/\\'/g, "'") + ",'";
915
+ .replace(c.escape || noMatch, function(match, code) {
916
+ return "',_.escape(" + unescape(code) + "),'";
917
+ })
918
+ .replace(c.interpolate || noMatch, function(match, code) {
919
+ return "'," + unescape(code) + ",'";
755
920
  })
756
- .replace(c.evaluate || null, function(match, code) {
757
- return "');" + code.replace(/\\'/g, "'")
758
- .replace(/[\r\n\t]/g, ' ') + "__p.push('";
921
+ .replace(c.evaluate || noMatch, function(match, code) {
922
+ return "');" + unescape(code).replace(/[\r\n\t]/g, ' ') + ";__p.push('";
759
923
  })
760
924
  .replace(/\r/g, '\\r')
761
925
  .replace(/\n/g, '\\n')
762
926
  .replace(/\t/g, '\\t')
763
927
  + "');}return __p.join('');";
764
- var func = new Function('obj', tmpl);
765
- return data ? func(data) : func;
928
+ var func = new Function('obj', '_', tmpl);
929
+ if (data) return func(data, _);
930
+ return function(data) {
931
+ return func.call(this, data, _);
932
+ };
933
+ };
934
+
935
+ // Add a "chain" function, which will delegate to the wrapper.
936
+ _.chain = function(obj) {
937
+ return _(obj).chain();
766
938
  };
767
939
 
768
940
  // The OOP Wrapper
@@ -797,8 +969,11 @@
797
969
  each(['pop', 'push', 'reverse', 'shift', 'sort', 'splice', 'unshift'], function(name) {
798
970
  var method = ArrayProto[name];
799
971
  wrapper.prototype[name] = function() {
800
- method.apply(this._wrapped, arguments);
801
- return result(this._wrapped, this._chain);
972
+ var wrapped = this._wrapped;
973
+ method.apply(wrapped, arguments);
974
+ var length = wrapped.length;
975
+ if ((name == 'shift' || name == 'splice') && length === 0) delete wrapped[0];
976
+ return result(wrapped, this._chain);
802
977
  };
803
978
  });
804
979
 
@@ -821,4 +996,4 @@
821
996
  return this._wrapped;
822
997
  };
823
998
 
824
- })();
999
+ }).call(this);