ultimate-base 0.6.2 → 0.7.2

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.
@@ -4,9 +4,13 @@ class Ultimate.Backbone.View extends Backbone.View
4
4
 
5
5
  @mixinNames: null
6
6
 
7
+ constructor: (options) ->
8
+ @reflectOptions options
9
+ super
10
+
7
11
  # Rest mixinNames
8
12
  @mixinable: ->
9
- @mixinNames = _.clone(@mixinNames)
13
+ @mixinNames = if @mixinNames then _.clone(@mixinNames) else []
10
14
 
11
15
  # Mixins support
12
16
  @include: (mixin, name = null) ->
@@ -17,11 +21,11 @@ class Ultimate.Backbone.View extends Backbone.View
17
21
  _.extend @::, mixin
18
22
 
19
23
  mixinsEvents: (events = {}) ->
20
- _.reduce( @constructor.mixinNames, ( (memo, name) -> _.extend(memo, _.result(@, _.string.camelize(name + 'Events'))) ), events, @ )
24
+ _.reduce( @constructor.mixinNames, ( (memo, name) -> _.extend(memo, _.result(@, name + 'Events')) ), events, @ )
21
25
 
22
26
  mixinsInitialize: ->
23
27
  for name in @constructor.mixinNames
24
- @[_.string.camelize(name + 'Initialize')]? arguments...
28
+ @[name + 'Initialize']? arguments...
25
29
  @
26
30
 
27
31
  # TODO comment for this trick
@@ -34,11 +38,6 @@ class Ultimate.Backbone.View extends Backbone.View
34
38
  throw new Error("__super can't find super method '#{methodName}'")
35
39
  superMethod.apply @, args
36
40
 
37
- # Overload parent method Backbone.View._configure() as hook for reflectOptions()
38
- _configure: (options) ->
39
- super
40
- @reflectOptions()
41
-
42
- reflectOptions: (options = @options) ->
41
+ reflectOptions: (options) ->
43
42
  @[attr] = value for attr, value of options when not _.isUndefined(@[attr])
44
43
  @
@@ -15,9 +15,9 @@ window.LOG_TODO ?= DEBUG_MODE
15
15
  method.apply(console, args)
16
16
  else
17
17
  method(args)
18
- return args[0]
18
+ args[0]
19
19
 
20
- @_cout = =>
20
+ @_cout = ->
21
21
  console.log(arguments) if console?
22
22
  arguments[0]
23
23
 
@@ -55,3 +55,16 @@ window.LOG_TODO ?= DEBUG_MODE
55
55
  ah[0] = ad.join('.')
56
56
  aq[0] = ah.join('#')
57
57
  aq.join('?')
58
+
59
+ @waitFor = (delay, times, check, success, fail) ->
60
+ startWaitFor = ->
61
+ setTimeout ( ->
62
+ times--
63
+ if check()
64
+ success?(times)
65
+ else if times > 0
66
+ startWaitFor()
67
+ else
68
+ fail?()
69
+ ), delay
70
+ startWaitFor()
@@ -1,15 +1,14 @@
1
- // Underscore.js 1.4.4
2
- // ===================
1
+ // Underscore.js 1.5.2
2
+ // http://underscorejs.org
3
+ // (c) 2009-2014 Jeremy Ashkenas, DocumentCloud and Investigative Reporters & Editors
4
+ // Underscore may be freely distributed under the MIT license.
3
5
 
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
6
  (function() {
11
7
 
12
- // Establish the root object, `window` in the browser, or `global` on the server.
8
+ // Baseline setup
9
+ // --------------
10
+
11
+ // Establish the root object, `window` in the browser, or `exports` on the server.
13
12
  var root = this;
14
13
 
15
14
  // Save the previous value of the `_` variable.
@@ -21,12 +20,18 @@
21
20
  // Save bytes in the minified (but not gzipped) version:
22
21
  var ArrayProto = Array.prototype, ObjProto = Object.prototype, FuncProto = Function.prototype;
23
22
 
23
+ //use the faster Date.now if available.
24
+ var getTime = (Date.now || function() {
25
+ return new Date().getTime();
26
+ });
27
+
24
28
  // 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;
29
+ var
30
+ push = ArrayProto.push,
31
+ slice = ArrayProto.slice,
32
+ concat = ArrayProto.concat,
33
+ toString = ObjProto.toString,
34
+ hasOwnProperty = ObjProto.hasOwnProperty;
30
35
 
31
36
  // All **ECMAScript 5** native function implementations that we hope to use
32
37
  // are declared here.
@@ -65,7 +70,7 @@
65
70
  }
66
71
 
67
72
  // Current version.
68
- _.VERSION = '1.4.4';
73
+ _.VERSION = '1.5.2';
69
74
 
70
75
  // Collection Functions
71
76
  // --------------------
@@ -78,14 +83,13 @@
78
83
  if (nativeForEach && obj.forEach === nativeForEach) {
79
84
  obj.forEach(iterator, context);
80
85
  } else if (obj.length === +obj.length) {
81
- for (var i = 0, l = obj.length; i < l; i++) {
86
+ for (var i = 0, length = obj.length; i < length; i++) {
82
87
  if (iterator.call(context, obj[i], i, obj) === breaker) return;
83
88
  }
84
89
  } else {
85
- for (var key in obj) {
86
- if (_.has(obj, key)) {
87
- if (iterator.call(context, obj[key], key, obj) === breaker) return;
88
- }
90
+ var keys = _.keys(obj);
91
+ for (var i = 0, length = keys.length; i < length; i++) {
92
+ if (iterator.call(context, obj[keys[i]], keys[i], obj) === breaker) return;
89
93
  }
90
94
  }
91
95
  };
@@ -97,7 +101,7 @@
97
101
  if (obj == null) return results;
98
102
  if (nativeMap && obj.map === nativeMap) return obj.map(iterator, context);
99
103
  each(obj, function(value, index, list) {
100
- results[results.length] = iterator.call(context, value, index, list);
104
+ results.push(iterator.call(context, value, index, list));
101
105
  });
102
106
  return results;
103
107
  };
@@ -172,7 +176,7 @@
172
176
  if (obj == null) return results;
173
177
  if (nativeFilter && obj.filter === nativeFilter) return obj.filter(iterator, context);
174
178
  each(obj, function(value, index, list) {
175
- if (iterator.call(context, value, index, list)) results[results.length] = value;
179
+ if (iterator.call(context, value, index, list)) results.push(value);
176
180
  });
177
181
  return results;
178
182
  };
@@ -233,13 +237,12 @@
233
237
 
234
238
  // Convenience version of a common use case of `map`: fetching a property.
235
239
  _.pluck = function(obj, key) {
236
- return _.map(obj, function(value){ return value[key]; });
240
+ return _.map(obj, _.property(key));
237
241
  };
238
242
 
239
243
  // Convenience version of a common use case of `filter`: selecting only objects
240
244
  // containing specific `key:value` pairs.
241
245
  _.where = function(obj, attrs, first) {
242
- if (_.isEmpty(attrs)) return first ? null : [];
243
246
  return _[first ? 'find' : 'filter'](obj, function(value) {
244
247
  for (var key in attrs) {
245
248
  if (attrs[key] !== value[key]) return false;
@@ -256,7 +259,7 @@
256
259
 
257
260
  // Return the maximum element or (element-based computation).
258
261
  // Can't optimize arrays of integers longer than 65,535 elements.
259
- // See: https://bugs.webkit.org/show_bug.cgi?id=80797
262
+ // See [WebKit Bug 80797](https://bugs.webkit.org/show_bug.cgi?id=80797)
260
263
  _.max = function(obj, iterator, context) {
261
264
  if (!iterator && _.isArray(obj) && obj[0] === +obj[0] && obj.length < 65535) {
262
265
  return Math.max.apply(Math, obj);
@@ -265,7 +268,7 @@
265
268
  var result = {computed : -Infinity, value: -Infinity};
266
269
  each(obj, function(value, index, list) {
267
270
  var computed = iterator ? iterator.call(context, value, index, list) : value;
268
- computed >= result.computed && (result = {value : value, computed : computed});
271
+ computed > result.computed && (result = {value : value, computed : computed});
269
272
  });
270
273
  return result.value;
271
274
  };
@@ -284,7 +287,8 @@
284
287
  return result.value;
285
288
  };
286
289
 
287
- // Shuffle an array.
290
+ // Shuffle an array, using the modern version of the
291
+ // [Fisher-Yates shuffle](http://en.wikipedia.org/wiki/Fisher–Yates_shuffle).
288
292
  _.shuffle = function(obj) {
289
293
  var rand;
290
294
  var index = 0;
@@ -297,19 +301,32 @@
297
301
  return shuffled;
298
302
  };
299
303
 
304
+ // Sample **n** random values from a collection.
305
+ // If **n** is not specified, returns a single random element.
306
+ // The internal `guard` argument allows it to work with `map`.
307
+ _.sample = function(obj, n, guard) {
308
+ if (n == null || guard) {
309
+ if (obj.length !== +obj.length) obj = _.values(obj);
310
+ return obj[_.random(obj.length - 1)];
311
+ }
312
+ return _.shuffle(obj).slice(0, Math.max(0, n));
313
+ };
314
+
300
315
  // An internal function to generate lookup iterators.
301
316
  var lookupIterator = function(value) {
302
- return _.isFunction(value) ? value : function(obj){ return obj[value]; };
317
+ if (value == null) return _.identity;
318
+ if (_.isFunction(value)) return value;
319
+ return _.property(value);
303
320
  };
304
321
 
305
322
  // Sort the object's values by a criterion produced by an iterator.
306
- _.sortBy = function(obj, value, context) {
307
- var iterator = lookupIterator(value);
323
+ _.sortBy = function(obj, iterator, context) {
324
+ iterator = lookupIterator(iterator);
308
325
  return _.pluck(_.map(obj, function(value, index, list) {
309
326
  return {
310
- value : value,
311
- index : index,
312
- criteria : iterator.call(context, value, index, list)
327
+ value: value,
328
+ index: index,
329
+ criteria: iterator.call(context, value, index, list)
313
330
  };
314
331
  }).sort(function(left, right) {
315
332
  var a = left.criteria;
@@ -318,43 +335,46 @@
318
335
  if (a > b || a === void 0) return 1;
319
336
  if (a < b || b === void 0) return -1;
320
337
  }
321
- return left.index < right.index ? -1 : 1;
338
+ return left.index - right.index;
322
339
  }), 'value');
323
340
  };
324
341
 
325
342
  // 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;
343
+ var group = function(behavior) {
344
+ return function(obj, iterator, context) {
345
+ var result = {};
346
+ iterator = lookupIterator(iterator);
347
+ each(obj, function(value, index) {
348
+ var key = iterator.call(context, value, index, obj);
349
+ behavior(result, key, value);
350
+ });
351
+ return result;
352
+ };
334
353
  };
335
354
 
336
355
  // Groups the object's values by a criterion. Pass either a string attribute
337
356
  // 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
- };
357
+ _.groupBy = group(function(result, key, value) {
358
+ _.has(result, key) ? result[key].push(value) : result[key] = [value];
359
+ });
360
+
361
+ // Indexes the object's values by a criterion, similar to `groupBy`, but for
362
+ // when you know that your index values will be unique.
363
+ _.indexBy = group(function(result, key, value) {
364
+ result[key] = value;
365
+ });
343
366
 
344
367
  // Counts instances of an object that group by a certain criterion. Pass
345
368
  // either a string attribute to count by, or a function that returns the
346
369
  // 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
- };
370
+ _.countBy = group(function(result, key) {
371
+ _.has(result, key) ? result[key]++ : result[key] = 1;
372
+ });
353
373
 
354
374
  // Use a comparator function to figure out the smallest index at which
355
375
  // an object should be inserted so as to maintain order. Uses binary search.
356
376
  _.sortedIndex = function(array, obj, iterator, context) {
357
- iterator = iterator == null ? _.identity : lookupIterator(iterator);
377
+ iterator = lookupIterator(iterator);
358
378
  var value = iterator.call(context, obj);
359
379
  var low = 0, high = array.length;
360
380
  while (low < high) {
@@ -364,7 +384,7 @@
364
384
  return low;
365
385
  };
366
386
 
367
- // Safely convert anything iterable into a real, live array.
387
+ // Safely create a real, live array from anything iterable.
368
388
  _.toArray = function(obj) {
369
389
  if (!obj) return [];
370
390
  if (_.isArray(obj)) return slice.call(obj);
@@ -386,7 +406,9 @@
386
406
  // allows it to work with `_.map`.
387
407
  _.first = _.head = _.take = function(array, n, guard) {
388
408
  if (array == null) return void 0;
389
- return (n != null) && !guard ? slice.call(array, 0, n) : array[0];
409
+ if ((n == null) || guard) return array[0];
410
+ if (n < 0) return [];
411
+ return slice.call(array, 0, n);
390
412
  };
391
413
 
392
414
  // Returns everything but the last entry of the array. Especially useful on
@@ -401,11 +423,8 @@
401
423
  // values in the array. The **guard** check allows it to work with `_.map`.
402
424
  _.last = function(array, n, guard) {
403
425
  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
- }
426
+ if ((n == null) || guard) return array[array.length - 1];
427
+ return slice.call(array, Math.max(array.length - n, 0));
409
428
  };
410
429
 
411
430
  // Returns everything but the first entry of the array. Aliased as `tail` and `drop`.
@@ -423,8 +442,11 @@
423
442
 
424
443
  // Internal implementation of a recursive `flatten` function.
425
444
  var flatten = function(input, shallow, output) {
445
+ if (shallow && _.every(input, _.isArray)) {
446
+ return concat.apply(output, input);
447
+ }
426
448
  each(input, function(value) {
427
- if (_.isArray(value)) {
449
+ if (_.isArray(value) || _.isArguments(value)) {
428
450
  shallow ? push.apply(output, value) : flatten(value, shallow, output);
429
451
  } else {
430
452
  output.push(value);
@@ -433,7 +455,7 @@
433
455
  return output;
434
456
  };
435
457
 
436
- // Return a completely flattened version of an array.
458
+ // Flatten out an array, either recursively (by default), or just one level.
437
459
  _.flatten = function(array, shallow) {
438
460
  return flatten(array, shallow, []);
439
461
  };
@@ -467,7 +489,7 @@
467
489
  // Produce an array that contains the union: each distinct element from all of
468
490
  // the passed-in arrays.
469
491
  _.union = function() {
470
- return _.uniq(concat.apply(ArrayProto, arguments));
492
+ return _.uniq(_.flatten(arguments, true));
471
493
  };
472
494
 
473
495
  // Produce an array that contains every item shared between all the
@@ -491,11 +513,10 @@
491
513
  // Zip together multiple lists into a single array -- elements that share
492
514
  // an index go together.
493
515
  _.zip = function() {
494
- var args = slice.call(arguments);
495
- var length = _.max(_.pluck(args, 'length'));
516
+ var length = _.max(_.pluck(arguments, "length").concat(0));
496
517
  var results = new Array(length);
497
518
  for (var i = 0; i < length; i++) {
498
- results[i] = _.pluck(args, "" + i);
519
+ results[i] = _.pluck(arguments, '' + i);
499
520
  }
500
521
  return results;
501
522
  };
@@ -506,7 +527,7 @@
506
527
  _.object = function(list, values) {
507
528
  if (list == null) return {};
508
529
  var result = {};
509
- for (var i = 0, l = list.length; i < l; i++) {
530
+ for (var i = 0, length = list.length; i < length; i++) {
510
531
  if (values) {
511
532
  result[list[i]] = values[i];
512
533
  } else {
@@ -524,17 +545,17 @@
524
545
  // for **isSorted** to use binary search.
525
546
  _.indexOf = function(array, item, isSorted) {
526
547
  if (array == null) return -1;
527
- var i = 0, l = array.length;
548
+ var i = 0, length = array.length;
528
549
  if (isSorted) {
529
550
  if (typeof isSorted == 'number') {
530
- i = (isSorted < 0 ? Math.max(0, l + isSorted) : isSorted);
551
+ i = (isSorted < 0 ? Math.max(0, length + isSorted) : isSorted);
531
552
  } else {
532
553
  i = _.sortedIndex(array, item);
533
554
  return array[i] === item ? i : -1;
534
555
  }
535
556
  }
536
557
  if (nativeIndexOf && array.indexOf === nativeIndexOf) return array.indexOf(item, isSorted);
537
- for (; i < l; i++) if (array[i] === item) return i;
558
+ for (; i < length; i++) if (array[i] === item) return i;
538
559
  return -1;
539
560
  };
540
561
 
@@ -560,11 +581,11 @@
560
581
  }
561
582
  step = arguments[2] || 1;
562
583
 
563
- var len = Math.max(Math.ceil((stop - start) / step), 0);
584
+ var length = Math.max(Math.ceil((stop - start) / step), 0);
564
585
  var idx = 0;
565
- var range = new Array(len);
586
+ var range = new Array(length);
566
587
 
567
- while(idx < len) {
588
+ while(idx < length) {
568
589
  range[idx++] = start;
569
590
  start += step;
570
591
  }
@@ -575,31 +596,51 @@
575
596
  // Function (ahem) Functions
576
597
  // ------------------
577
598
 
599
+ // Reusable constructor function for prototype setting.
600
+ var ctor = function(){};
601
+
578
602
  // Create a function bound to a given object (assigning `this`, and arguments,
579
603
  // optionally). Delegates to **ECMAScript 5**'s native `Function.bind` if
580
604
  // available.
581
605
  _.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)));
606
+ var args, bound;
607
+ if (nativeBind && func.bind === nativeBind) return nativeBind.apply(func, slice.call(arguments, 1));
608
+ if (!_.isFunction(func)) throw new TypeError;
609
+ args = slice.call(arguments, 2);
610
+ return bound = function() {
611
+ if (!(this instanceof bound)) return func.apply(context, args.concat(slice.call(arguments)));
612
+ ctor.prototype = func.prototype;
613
+ var self = new ctor;
614
+ ctor.prototype = null;
615
+ var result = func.apply(self, args.concat(slice.call(arguments)));
616
+ if (Object(result) === result) return result;
617
+ return self;
586
618
  };
587
619
  };
588
620
 
589
621
  // Partially apply a function by creating a version that has had some of its
590
- // arguments pre-filled, without changing its dynamic `this` context.
622
+ // arguments pre-filled, without changing its dynamic `this` context. _ acts
623
+ // as a placeholder, allowing any combination of arguments to be pre-filled.
591
624
  _.partial = function(func) {
592
- var args = slice.call(arguments, 1);
625
+ var boundArgs = slice.call(arguments, 1);
593
626
  return function() {
594
- return func.apply(this, args.concat(slice.call(arguments)));
627
+ var args = slice.call(boundArgs);
628
+ _.each(arguments, function(arg) {
629
+ var index = args.indexOf(_);
630
+ args[index >= 0 ? index : args.length] = arg;
631
+ });
632
+ return func.apply(this, _.map(args, function(value) {
633
+ return value === _ ? void 0 : value;
634
+ }));
595
635
  };
596
636
  };
597
637
 
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.
638
+ // Bind a number of an object's methods to that object. Remaining arguments
639
+ // are the method names to be bound. Useful for ensuring that all callbacks
640
+ // defined on an object belong to it.
600
641
  _.bindAll = function(obj) {
601
642
  var funcs = slice.call(arguments, 1);
602
- if (funcs.length === 0) funcs = _.functions(obj);
643
+ if (funcs.length === 0) throw new Error("bindAll must be passed function names");
603
644
  each(funcs, function(f) { obj[f] = _.bind(obj[f], obj); });
604
645
  return obj;
605
646
  };
@@ -628,17 +669,24 @@
628
669
  };
629
670
 
630
671
  // 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;
672
+ // during a given window of time. Normally, the throttled function will run
673
+ // as much as it can, without ever going more than once per `wait` duration;
674
+ // but if you'd like to disable the execution on the leading edge, pass
675
+ // `{leading: false}`. To disable execution on the trailing edge, ditto.
676
+ _.throttle = function(func, wait, options) {
677
+ var context, args, result;
678
+ var timeout = null;
634
679
  var previous = 0;
680
+ options || (options = {});
635
681
  var later = function() {
636
- previous = new Date;
682
+ previous = options.leading === false ? 0 : getTime();
637
683
  timeout = null;
638
684
  result = func.apply(context, args);
685
+ context = args = null;
639
686
  };
640
687
  return function() {
641
- var now = new Date;
688
+ var now = getTime();
689
+ if (!previous && options.leading === false) previous = now;
642
690
  var remaining = wait - (now - previous);
643
691
  context = this;
644
692
  args = arguments;
@@ -647,7 +695,8 @@
647
695
  timeout = null;
648
696
  previous = now;
649
697
  result = func.apply(context, args);
650
- } else if (!timeout) {
698
+ context = args = null;
699
+ } else if (!timeout && options.trailing !== false) {
651
700
  timeout = setTimeout(later, remaining);
652
701
  }
653
702
  return result;
@@ -659,17 +708,32 @@
659
708
  // N milliseconds. If `immediate` is passed, trigger the function on the
660
709
  // leading edge, instead of the trailing.
661
710
  _.debounce = function(func, wait, immediate) {
662
- var timeout, result;
711
+ var timeout, args, context, timestamp, result;
663
712
  return function() {
664
- var context = this, args = arguments;
713
+ context = this;
714
+ args = arguments;
715
+ timestamp = getTime();
665
716
  var later = function() {
666
- timeout = null;
667
- if (!immediate) result = func.apply(context, args);
717
+ var last = getTime() - timestamp;
718
+ if (last < wait) {
719
+ timeout = setTimeout(later, wait - last);
720
+ } else {
721
+ timeout = null;
722
+ if (!immediate) {
723
+ result = func.apply(context, args);
724
+ context = args = null;
725
+ }
726
+ }
668
727
  };
669
728
  var callNow = immediate && !timeout;
670
- clearTimeout(timeout);
671
- timeout = setTimeout(later, wait);
672
- if (callNow) result = func.apply(context, args);
729
+ if (!timeout) {
730
+ timeout = setTimeout(later, wait);
731
+ }
732
+ if (callNow) {
733
+ result = func.apply(context, args);
734
+ context = args = null;
735
+ }
736
+
673
737
  return result;
674
738
  };
675
739
  };
@@ -691,11 +755,7 @@
691
755
  // allowing you to adjust arguments, run code before and after, and
692
756
  // conditionally execute the original function.
693
757
  _.wrap = function(func, wrapper) {
694
- return function() {
695
- var args = [func];
696
- push.apply(args, arguments);
697
- return wrapper.apply(this, args);
698
- };
758
+ return _.partial(wrapper, func);
699
759
  };
700
760
 
701
761
  // Returns a function that is the composition of a list of functions, each
@@ -713,7 +773,6 @@
713
773
 
714
774
  // Returns a function that will only be executed after being called N times.
715
775
  _.after = function(times, func) {
716
- if (times <= 0) return func();
717
776
  return function() {
718
777
  if (--times < 1) {
719
778
  return func.apply(this, arguments);
@@ -729,28 +788,39 @@
729
788
  _.keys = nativeKeys || function(obj) {
730
789
  if (obj !== Object(obj)) throw new TypeError('Invalid object');
731
790
  var keys = [];
732
- for (var key in obj) if (_.has(obj, key)) keys[keys.length] = key;
791
+ for (var key in obj) if (_.has(obj, key)) keys.push(key);
733
792
  return keys;
734
793
  };
735
794
 
736
795
  // Retrieve the values of an object's properties.
737
796
  _.values = function(obj) {
738
- var values = [];
739
- for (var key in obj) if (_.has(obj, key)) values.push(obj[key]);
797
+ var keys = _.keys(obj);
798
+ var length = keys.length;
799
+ var values = new Array(length);
800
+ for (var i = 0; i < length; i++) {
801
+ values[i] = obj[keys[i]];
802
+ }
740
803
  return values;
741
804
  };
742
805
 
743
806
  // Convert an object into a list of `[key, value]` pairs.
744
807
  _.pairs = function(obj) {
745
- var pairs = [];
746
- for (var key in obj) if (_.has(obj, key)) pairs.push([key, obj[key]]);
808
+ var keys = _.keys(obj);
809
+ var length = keys.length;
810
+ var pairs = new Array(length);
811
+ for (var i = 0; i < length; i++) {
812
+ pairs[i] = [keys[i], obj[keys[i]]];
813
+ }
747
814
  return pairs;
748
815
  };
749
816
 
750
817
  // Invert the keys and values of an object. The values must be serializable.
751
818
  _.invert = function(obj) {
752
819
  var result = {};
753
- for (var key in obj) if (_.has(obj, key)) result[obj[key]] = key;
820
+ var keys = _.keys(obj);
821
+ for (var i = 0, length = keys.length; i < length; i++) {
822
+ result[obj[keys[i]]] = keys[i];
823
+ }
754
824
  return result;
755
825
  };
756
826
 
@@ -801,7 +871,7 @@
801
871
  each(slice.call(arguments, 1), function(source) {
802
872
  if (source) {
803
873
  for (var prop in source) {
804
- if (obj[prop] == null) obj[prop] = source[prop];
874
+ if (obj[prop] === void 0) obj[prop] = source[prop];
805
875
  }
806
876
  }
807
877
  });
@@ -825,7 +895,7 @@
825
895
  // Internal recursive comparison function for `isEqual`.
826
896
  var eq = function(a, b, aStack, bStack) {
827
897
  // 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.
898
+ // See the [Harmony `egal` proposal](http://wiki.ecmascript.org/doku.php?id=harmony:egal).
829
899
  if (a === b) return a !== 0 || 1 / a == 1 / b;
830
900
  // A strict comparison is necessary because `null == undefined`.
831
901
  if (a == null || b == null) return a === b;
@@ -867,6 +937,14 @@
867
937
  // unique nested structures.
868
938
  if (aStack[length] == a) return bStack[length] == b;
869
939
  }
940
+ // Objects with different constructors are not equivalent, but `Object`s
941
+ // from different frames are.
942
+ var aCtor = a.constructor, bCtor = b.constructor;
943
+ if (aCtor !== bCtor && !(_.isFunction(aCtor) && (aCtor instanceof aCtor) &&
944
+ _.isFunction(bCtor) && (bCtor instanceof bCtor))
945
+ && ('constructor' in a && 'constructor' in b)) {
946
+ return false;
947
+ }
870
948
  // Add the first object to the stack of traversed objects.
871
949
  aStack.push(a);
872
950
  bStack.push(b);
@@ -883,13 +961,6 @@
883
961
  }
884
962
  }
885
963
  } 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
964
  // Deep compare objects.
894
965
  for (var key in a) {
895
966
  if (_.has(a, key)) {
@@ -1011,9 +1082,21 @@
1011
1082
  return value;
1012
1083
  };
1013
1084
 
1085
+ _.constant = function(value) {
1086
+ return function () {
1087
+ return value;
1088
+ };
1089
+ };
1090
+
1091
+ _.property = function(key) {
1092
+ return function(obj) {
1093
+ return obj[key];
1094
+ };
1095
+ };
1096
+
1014
1097
  // Run a function **n** times.
1015
1098
  _.times = function(n, iterator, context) {
1016
- var accum = Array(n);
1099
+ var accum = Array(Math.max(0, n));
1017
1100
  for (var i = 0; i < n; i++) accum[i] = iterator.call(context, i);
1018
1101
  return accum;
1019
1102
  };
@@ -1034,8 +1117,7 @@
1034
1117
  '<': '&lt;',
1035
1118
  '>': '&gt;',
1036
1119
  '"': '&quot;',
1037
- "'": '&#x27;',
1038
- '/': '&#x2F;'
1120
+ "'": '&#x27;'
1039
1121
  }
1040
1122
  };
1041
1123
  entityMap.unescape = _.invert(entityMap.escape);
@@ -1056,17 +1138,17 @@
1056
1138
  };
1057
1139
  });
1058
1140
 
1059
- // If the value of the named property is a function then invoke it;
1060
- // otherwise, return it.
1141
+ // If the value of the named `property` is a function then invoke it with the
1142
+ // `object` as context; otherwise, return it.
1061
1143
  _.result = function(object, property) {
1062
- if (object == null) return null;
1144
+ if (object == null) return void 0;
1063
1145
  var value = object[property];
1064
1146
  return _.isFunction(value) ? value.call(object) : value;
1065
1147
  };
1066
1148
 
1067
1149
  // Add your own custom functions to the Underscore object.
1068
1150
  _.mixin = function(obj) {
1069
- each(_.functions(obj), function(name){
1151
+ each(_.functions(obj), function(name) {
1070
1152
  var func = _[name] = obj[name];
1071
1153
  _.prototype[name] = function() {
1072
1154
  var args = [this._wrapped];
@@ -1224,4 +1306,16 @@
1224
1306
 
1225
1307
  });
1226
1308
 
1309
+ // AMD registration happens at the end for compatibility with AMD loaders
1310
+ // that may not enforce next-turn semantics on modules. Even though general
1311
+ // practice for AMD registration is to be anonymous, underscore registers
1312
+ // as a named module because, like jQuery, it is a base library that is
1313
+ // popular enough to be bundled in a third party lib, but not be part of
1314
+ // an AMD load request. Those cases could generate an error when an
1315
+ // anonymous define() is called outside of a loader request.
1316
+ if (typeof define === 'function' && define.amd) {
1317
+ define('underscore', [], function() {
1318
+ return _;
1319
+ });
1320
+ }
1227
1321
  }).call(this);