rails-backbone-generator 0.0.3 → 0.1.4

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