rails-backbone 0.7.2 → 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,68 +1,32 @@
1
- (function() {
2
- var methodMap = {
3
- 'create': 'POST',
4
- 'update': 'PUT',
5
- 'delete': 'DELETE',
6
- 'read' : 'GET'
7
- };
8
-
9
- var getUrl = function(object) {
10
- if (!(object && object.url)) return null;
11
- return _.isFunction(object.url) ? object.url() : object.url;
12
- };
13
-
14
- var urlError = function() {
15
- throw new Error("A 'url' property or function must be specified");
16
- };
1
+ (function($) {
2
+ Backbone._sync = Backbone.sync;
17
3
 
18
4
  Backbone.sync = function(method, model, options) {
19
- var type = methodMap[method];
5
+ if (!options.noCSRF) {
6
+ var beforeSend = options.beforeSend;
20
7
 
21
- // Default JSON-request options.
22
- var params = _.extend({
23
- type: type,
24
- dataType: 'json',
25
- beforeSend: function( xhr ) {
8
+ // Set X-CSRF-Token HTTP header
9
+ options.beforeSend = function(xhr) {
26
10
  var token = $('meta[name="csrf-token"]').attr('content');
27
11
  if (token) xhr.setRequestHeader('X-CSRF-Token', token);
28
-
29
- model.trigger('sync:start');
30
- }
31
- }, options);
32
-
33
- if (!params.url) {
34
- params.url = getUrl(model) || urlError();
12
+ if (beforeSend) return beforeSend.apply(this, arguments);
13
+ };
35
14
  }
36
15
 
37
- // Ensure that we have the appropriate request data.
38
- if (!params.data && model && (method == 'create' || method == 'update')) {
39
- params.contentType = 'application/json';
40
-
41
- var data = {}
42
-
43
- if(model.paramRoot) {
44
- data[model.paramRoot] = model.toJSON();
16
+ // Serialize data, optionally using paramRoot
17
+ if (options.data == null && model && (method === 'create' || method === 'update' || method === 'patch')) {
18
+ options.contentType = 'application/json';
19
+ data = JSON.stringify(options.attrs || model.toJSON(options));
20
+ if (model.paramRoot) {
21
+ data = {};
22
+ data[model.paramRoot] = model.toJSON(options);
45
23
  } else {
46
24
  data = model.toJSON();
47
25
  }
48
-
49
- params.data = JSON.stringify(data)
26
+ options.data = JSON.stringify(data);
50
27
  }
51
28
 
52
- // Don't process data on a non-GET request.
53
- if (params.type !== 'GET') {
54
- params.processData = false;
55
- }
29
+ return Backbone._sync(method, model, options);
30
+ };
56
31
 
57
- // Trigger the sync end event
58
- var complete = options.complete;
59
- params.complete = function(jqXHR, textStatus) {
60
- model.trigger('sync:end');
61
- if (complete) complete(jqXHR, textStatus);
62
- };
63
-
64
- // Make the request.
65
- return $.ajax(params);
66
- }
67
-
68
- }).call(this);
32
+ })(jQuery);
@@ -1,314 +1,450 @@
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.8.3
2
+ // http://underscorejs.org
3
+ // (c) 2009-2015 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.
18
15
  var previousUnderscore = root._;
19
16
 
20
- // Establish the object that gets returned to break out of a loop iteration.
21
- var breaker = {};
22
-
23
17
  // Save bytes in the minified (but not gzipped) version:
24
18
  var ArrayProto = Array.prototype, ObjProto = Object.prototype, FuncProto = Function.prototype;
25
19
 
26
20
  // 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;
21
+ var
22
+ push = ArrayProto.push,
23
+ slice = ArrayProto.slice,
24
+ toString = ObjProto.toString,
25
+ hasOwnProperty = ObjProto.hasOwnProperty;
31
26
 
32
27
  // All **ECMAScript 5** native function implementations that we hope to use
33
28
  // are declared here.
34
29
  var
35
- nativeForEach = ArrayProto.forEach,
36
- nativeMap = ArrayProto.map,
37
- nativeReduce = ArrayProto.reduce,
38
- nativeReduceRight = ArrayProto.reduceRight,
39
- nativeFilter = ArrayProto.filter,
40
- nativeEvery = ArrayProto.every,
41
- nativeSome = ArrayProto.some,
42
- nativeIndexOf = ArrayProto.indexOf,
43
- nativeLastIndexOf = ArrayProto.lastIndexOf,
44
30
  nativeIsArray = Array.isArray,
45
31
  nativeKeys = Object.keys,
46
- nativeBind = FuncProto.bind;
32
+ nativeBind = FuncProto.bind,
33
+ nativeCreate = Object.create;
34
+
35
+ // Naked function reference for surrogate-prototype-swapping.
36
+ var Ctor = function(){};
47
37
 
48
38
  // Create a safe reference to the Underscore object for use below.
49
- var _ = function(obj) { return new wrapper(obj); };
39
+ var _ = function(obj) {
40
+ if (obj instanceof _) return obj;
41
+ if (!(this instanceof _)) return new _(obj);
42
+ this._wrapped = obj;
43
+ };
50
44
 
51
45
  // Export the Underscore object for **Node.js**, with
52
46
  // 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.
47
+ // the browser, add `_` as a global object.
55
48
  if (typeof exports !== 'undefined') {
56
49
  if (typeof module !== 'undefined' && module.exports) {
57
50
  exports = module.exports = _;
58
51
  }
59
52
  exports._ = _;
60
53
  } else {
61
- root['_'] = _;
54
+ root._ = _;
62
55
  }
63
56
 
64
57
  // Current version.
65
- _.VERSION = '1.3.3';
58
+ _.VERSION = '1.8.3';
59
+
60
+ // Internal function that returns an efficient (for current engines) version
61
+ // of the passed-in callback, to be repeatedly applied in other Underscore
62
+ // functions.
63
+ var optimizeCb = function(func, context, argCount) {
64
+ if (context === void 0) return func;
65
+ switch (argCount == null ? 3 : argCount) {
66
+ case 1: return function(value) {
67
+ return func.call(context, value);
68
+ };
69
+ case 2: return function(value, other) {
70
+ return func.call(context, value, other);
71
+ };
72
+ case 3: return function(value, index, collection) {
73
+ return func.call(context, value, index, collection);
74
+ };
75
+ case 4: return function(accumulator, value, index, collection) {
76
+ return func.call(context, accumulator, value, index, collection);
77
+ };
78
+ }
79
+ return function() {
80
+ return func.apply(context, arguments);
81
+ };
82
+ };
83
+
84
+ // A mostly-internal function to generate callbacks that can be applied
85
+ // to each element in a collection, returning the desired result — either
86
+ // identity, an arbitrary callback, a property matcher, or a property accessor.
87
+ var cb = function(value, context, argCount) {
88
+ if (value == null) return _.identity;
89
+ if (_.isFunction(value)) return optimizeCb(value, context, argCount);
90
+ if (_.isObject(value)) return _.matcher(value);
91
+ return _.property(value);
92
+ };
93
+ _.iteratee = function(value, context) {
94
+ return cb(value, context, Infinity);
95
+ };
96
+
97
+ // An internal function for creating assigner functions.
98
+ var createAssigner = function(keysFunc, undefinedOnly) {
99
+ return function(obj) {
100
+ var length = arguments.length;
101
+ if (length < 2 || obj == null) return obj;
102
+ for (var index = 1; index < length; index++) {
103
+ var source = arguments[index],
104
+ keys = keysFunc(source),
105
+ l = keys.length;
106
+ for (var i = 0; i < l; i++) {
107
+ var key = keys[i];
108
+ if (!undefinedOnly || obj[key] === void 0) obj[key] = source[key];
109
+ }
110
+ }
111
+ return obj;
112
+ };
113
+ };
114
+
115
+ // An internal function for creating a new object that inherits from another.
116
+ var baseCreate = function(prototype) {
117
+ if (!_.isObject(prototype)) return {};
118
+ if (nativeCreate) return nativeCreate(prototype);
119
+ Ctor.prototype = prototype;
120
+ var result = new Ctor;
121
+ Ctor.prototype = null;
122
+ return result;
123
+ };
124
+
125
+ var property = function(key) {
126
+ return function(obj) {
127
+ return obj == null ? void 0 : obj[key];
128
+ };
129
+ };
130
+
131
+ // Helper for collection methods to determine whether a collection
132
+ // should be iterated as an array or as an object
133
+ // Related: http://people.mozilla.org/~jorendorff/es6-draft.html#sec-tolength
134
+ // Avoids a very nasty iOS 8 JIT bug on ARM-64. #2094
135
+ var MAX_ARRAY_INDEX = Math.pow(2, 53) - 1;
136
+ var getLength = property('length');
137
+ var isArrayLike = function(collection) {
138
+ var length = getLength(collection);
139
+ return typeof length == 'number' && length >= 0 && length <= MAX_ARRAY_INDEX;
140
+ };
66
141
 
67
142
  // Collection Functions
68
143
  // --------------------
69
144
 
70
145
  // The cornerstone, an `each` implementation, aka `forEach`.
71
- // Handles objects with the built-in `forEach`, arrays, and raw objects.
72
- // Delegates to **ECMAScript 5**'s native `forEach` if available.
73
- var each = _.each = _.forEach = function(obj, iterator, context) {
74
- if (obj == null) return;
75
- if (nativeForEach && obj.forEach === nativeForEach) {
76
- obj.forEach(iterator, context);
77
- } 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;
146
+ // Handles raw objects in addition to array-likes. Treats all
147
+ // sparse array-likes as if they were dense.
148
+ _.each = _.forEach = function(obj, iteratee, context) {
149
+ iteratee = optimizeCb(iteratee, context);
150
+ var i, length;
151
+ if (isArrayLike(obj)) {
152
+ for (i = 0, length = obj.length; i < length; i++) {
153
+ iteratee(obj[i], i, obj);
80
154
  }
81
155
  } else {
82
- for (var key in obj) {
83
- if (_.has(obj, key)) {
84
- if (iterator.call(context, obj[key], key, obj) === breaker) return;
85
- }
156
+ var keys = _.keys(obj);
157
+ for (i = 0, length = keys.length; i < length; i++) {
158
+ iteratee(obj[keys[i]], keys[i], obj);
86
159
  }
87
160
  }
161
+ return obj;
88
162
  };
89
163
 
90
- // Return the results of applying the iterator to each element.
91
- // Delegates to **ECMAScript 5**'s native `map` if available.
92
- _.map = _.collect = function(obj, iterator, context) {
93
- var results = [];
94
- if (obj == null) return results;
95
- if (nativeMap && obj.map === nativeMap) return obj.map(iterator, context);
96
- each(obj, function(value, index, list) {
97
- results[results.length] = iterator.call(context, value, index, list);
98
- });
99
- if (obj.length === +obj.length) results.length = obj.length;
164
+ // Return the results of applying the iteratee to each element.
165
+ _.map = _.collect = function(obj, iteratee, context) {
166
+ iteratee = cb(iteratee, context);
167
+ var keys = !isArrayLike(obj) && _.keys(obj),
168
+ length = (keys || obj).length,
169
+ results = Array(length);
170
+ for (var index = 0; index < length; index++) {
171
+ var currentKey = keys ? keys[index] : index;
172
+ results[index] = iteratee(obj[currentKey], currentKey, obj);
173
+ }
100
174
  return results;
101
175
  };
102
176
 
103
- // **Reduce** builds up a single result from a list of values, aka `inject`,
104
- // or `foldl`. Delegates to **ECMAScript 5**'s native `reduce` if available.
105
- _.reduce = _.foldl = _.inject = function(obj, iterator, memo, context) {
106
- var initial = arguments.length > 2;
107
- if (obj == null) obj = [];
108
- if (nativeReduce && obj.reduce === nativeReduce) {
109
- if (context) iterator = _.bind(iterator, context);
110
- return initial ? obj.reduce(iterator, memo) : obj.reduce(iterator);
177
+ // Create a reducing function iterating left or right.
178
+ function createReduce(dir) {
179
+ // Optimized iterator function as using arguments.length
180
+ // in the main function will deoptimize the, see #1991.
181
+ function iterator(obj, iteratee, memo, keys, index, length) {
182
+ for (; index >= 0 && index < length; index += dir) {
183
+ var currentKey = keys ? keys[index] : index;
184
+ memo = iteratee(memo, obj[currentKey], currentKey, obj);
185
+ }
186
+ return memo;
111
187
  }
112
- each(obj, function(value, index, list) {
113
- if (!initial) {
114
- memo = value;
115
- initial = true;
116
- } else {
117
- memo = iterator.call(context, memo, value, index, list);
188
+
189
+ return function(obj, iteratee, memo, context) {
190
+ iteratee = optimizeCb(iteratee, context, 4);
191
+ var keys = !isArrayLike(obj) && _.keys(obj),
192
+ length = (keys || obj).length,
193
+ index = dir > 0 ? 0 : length - 1;
194
+ // Determine the initial value if none is provided.
195
+ if (arguments.length < 3) {
196
+ memo = obj[keys ? keys[index] : index];
197
+ index += dir;
118
198
  }
119
- });
120
- if (!initial) throw new TypeError('Reduce of empty array with no initial value');
121
- return memo;
122
- };
199
+ return iterator(obj, iteratee, memo, keys, index, length);
200
+ };
201
+ }
202
+
203
+ // **Reduce** builds up a single result from a list of values, aka `inject`,
204
+ // or `foldl`.
205
+ _.reduce = _.foldl = _.inject = createReduce(1);
123
206
 
124
207
  // The right-associative version of reduce, also known as `foldr`.
125
- // Delegates to **ECMAScript 5**'s native `reduceRight` if available.
126
- _.reduceRight = _.foldr = function(obj, iterator, memo, context) {
127
- var initial = arguments.length > 2;
128
- if (obj == null) obj = [];
129
- if (nativeReduceRight && obj.reduceRight === nativeReduceRight) {
130
- if (context) iterator = _.bind(iterator, context);
131
- return initial ? obj.reduceRight(iterator, memo) : obj.reduceRight(iterator);
132
- }
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
- };
208
+ _.reduceRight = _.foldr = createReduce(-1);
137
209
 
138
210
  // Return the first value which passes a truth test. Aliased as `detect`.
139
- _.find = _.detect = function(obj, iterator, context) {
140
- var result;
141
- any(obj, function(value, index, list) {
142
- if (iterator.call(context, value, index, list)) {
143
- result = value;
144
- return true;
145
- }
146
- });
147
- return result;
211
+ _.find = _.detect = function(obj, predicate, context) {
212
+ var key;
213
+ if (isArrayLike(obj)) {
214
+ key = _.findIndex(obj, predicate, context);
215
+ } else {
216
+ key = _.findKey(obj, predicate, context);
217
+ }
218
+ if (key !== void 0 && key !== -1) return obj[key];
148
219
  };
149
220
 
150
221
  // Return all the elements that pass a truth test.
151
- // Delegates to **ECMAScript 5**'s native `filter` if available.
152
222
  // Aliased as `select`.
153
- _.filter = _.select = function(obj, iterator, context) {
223
+ _.filter = _.select = function(obj, predicate, context) {
154
224
  var results = [];
155
- if (obj == null) return results;
156
- if (nativeFilter && obj.filter === nativeFilter) return obj.filter(iterator, context);
157
- each(obj, function(value, index, list) {
158
- if (iterator.call(context, value, index, list)) results[results.length] = value;
225
+ predicate = cb(predicate, context);
226
+ _.each(obj, function(value, index, list) {
227
+ if (predicate(value, index, list)) results.push(value);
159
228
  });
160
229
  return results;
161
230
  };
162
231
 
163
232
  // Return all the elements for which a truth test fails.
164
- _.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;
233
+ _.reject = function(obj, predicate, context) {
234
+ return _.filter(obj, _.negate(cb(predicate)), context);
171
235
  };
172
236
 
173
237
  // Determine whether all of the elements match a truth test.
174
- // Delegates to **ECMAScript 5**'s native `every` if available.
175
238
  // Aliased as `all`.
176
- _.every = _.all = function(obj, iterator, context) {
177
- var result = true;
178
- if (obj == null) return result;
179
- if (nativeEvery && obj.every === nativeEvery) return obj.every(iterator, context);
180
- each(obj, function(value, index, list) {
181
- if (!(result = result && iterator.call(context, value, index, list))) return breaker;
182
- });
183
- return !!result;
239
+ _.every = _.all = function(obj, predicate, context) {
240
+ predicate = cb(predicate, context);
241
+ var keys = !isArrayLike(obj) && _.keys(obj),
242
+ length = (keys || obj).length;
243
+ for (var index = 0; index < length; index++) {
244
+ var currentKey = keys ? keys[index] : index;
245
+ if (!predicate(obj[currentKey], currentKey, obj)) return false;
246
+ }
247
+ return true;
184
248
  };
185
249
 
186
250
  // Determine if at least one element in the object matches a truth test.
187
- // Delegates to **ECMAScript 5**'s native `some` if available.
188
251
  // Aliased as `any`.
189
- var any = _.some = _.any = function(obj, iterator, context) {
190
- iterator || (iterator = _.identity);
191
- var result = false;
192
- if (obj == null) return result;
193
- if (nativeSome && obj.some === nativeSome) return obj.some(iterator, context);
194
- each(obj, function(value, index, list) {
195
- if (result || (result = iterator.call(context, value, index, list))) return breaker;
196
- });
197
- return !!result;
252
+ _.some = _.any = function(obj, predicate, context) {
253
+ predicate = cb(predicate, context);
254
+ var keys = !isArrayLike(obj) && _.keys(obj),
255
+ length = (keys || obj).length;
256
+ for (var index = 0; index < length; index++) {
257
+ var currentKey = keys ? keys[index] : index;
258
+ if (predicate(obj[currentKey], currentKey, obj)) return true;
259
+ }
260
+ return false;
198
261
  };
199
262
 
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;
205
- if (nativeIndexOf && obj.indexOf === nativeIndexOf) return obj.indexOf(target) != -1;
206
- found = any(obj, function(value) {
207
- return value === target;
208
- });
209
- return found;
263
+ // Determine if the array or object contains a given item (using `===`).
264
+ // Aliased as `includes` and `include`.
265
+ _.contains = _.includes = _.include = function(obj, item, fromIndex, guard) {
266
+ if (!isArrayLike(obj)) obj = _.values(obj);
267
+ if (typeof fromIndex != 'number' || guard) fromIndex = 0;
268
+ return _.indexOf(obj, item, fromIndex) >= 0;
210
269
  };
211
270
 
212
271
  // Invoke a method (with arguments) on every item in a collection.
213
272
  _.invoke = function(obj, method) {
214
273
  var args = slice.call(arguments, 2);
274
+ var isFunc = _.isFunction(method);
215
275
  return _.map(obj, function(value) {
216
- return (_.isFunction(method) ? method || value : value[method]).apply(value, args);
276
+ var func = isFunc ? method : value[method];
277
+ return func == null ? func : func.apply(value, args);
217
278
  });
218
279
  };
219
280
 
220
281
  // Convenience version of a common use case of `map`: fetching a property.
221
282
  _.pluck = function(obj, key) {
222
- return _.map(obj, function(value){ return value[key]; });
283
+ return _.map(obj, _.property(key));
223
284
  };
224
285
 
225
- // Return the maximum element or (element-based computation).
226
- _.max = function(obj, iterator, context) {
227
- if (!iterator && _.isArray(obj) && obj[0] === +obj[0]) return Math.max.apply(Math, obj);
228
- if (!iterator && _.isEmpty(obj)) return -Infinity;
229
- var result = {computed : -Infinity};
230
- each(obj, function(value, index, list) {
231
- var computed = iterator ? iterator.call(context, value, index, list) : value;
232
- computed >= result.computed && (result = {value : value, computed : computed});
233
- });
234
- return result.value;
286
+ // Convenience version of a common use case of `filter`: selecting only objects
287
+ // containing specific `key:value` pairs.
288
+ _.where = function(obj, attrs) {
289
+ return _.filter(obj, _.matcher(attrs));
290
+ };
291
+
292
+ // Convenience version of a common use case of `find`: getting the first object
293
+ // containing specific `key:value` pairs.
294
+ _.findWhere = function(obj, attrs) {
295
+ return _.find(obj, _.matcher(attrs));
296
+ };
297
+
298
+ // Return the maximum element (or element-based computation).
299
+ _.max = function(obj, iteratee, context) {
300
+ var result = -Infinity, lastComputed = -Infinity,
301
+ value, computed;
302
+ if (iteratee == null && obj != null) {
303
+ obj = isArrayLike(obj) ? obj : _.values(obj);
304
+ for (var i = 0, length = obj.length; i < length; i++) {
305
+ value = obj[i];
306
+ if (value > result) {
307
+ result = value;
308
+ }
309
+ }
310
+ } else {
311
+ iteratee = cb(iteratee, context);
312
+ _.each(obj, function(value, index, list) {
313
+ computed = iteratee(value, index, list);
314
+ if (computed > lastComputed || computed === -Infinity && result === -Infinity) {
315
+ result = value;
316
+ lastComputed = computed;
317
+ }
318
+ });
319
+ }
320
+ return result;
235
321
  };
236
322
 
237
323
  // Return the minimum element (or element-based computation).
238
- _.min = function(obj, iterator, context) {
239
- if (!iterator && _.isArray(obj) && obj[0] === +obj[0]) return Math.min.apply(Math, obj);
240
- if (!iterator && _.isEmpty(obj)) return Infinity;
241
- var result = {computed : Infinity};
242
- each(obj, function(value, index, list) {
243
- var computed = iterator ? iterator.call(context, value, index, list) : value;
244
- computed < result.computed && (result = {value : value, computed : computed});
245
- });
246
- return result.value;
324
+ _.min = function(obj, iteratee, context) {
325
+ var result = Infinity, lastComputed = Infinity,
326
+ value, computed;
327
+ if (iteratee == null && obj != null) {
328
+ obj = isArrayLike(obj) ? obj : _.values(obj);
329
+ for (var i = 0, length = obj.length; i < length; i++) {
330
+ value = obj[i];
331
+ if (value < result) {
332
+ result = value;
333
+ }
334
+ }
335
+ } else {
336
+ iteratee = cb(iteratee, context);
337
+ _.each(obj, function(value, index, list) {
338
+ computed = iteratee(value, index, list);
339
+ if (computed < lastComputed || computed === Infinity && result === Infinity) {
340
+ result = value;
341
+ lastComputed = computed;
342
+ }
343
+ });
344
+ }
345
+ return result;
247
346
  };
248
347
 
249
- // Shuffle an array.
348
+ // Shuffle a collection, using the modern version of the
349
+ // [Fisher-Yates shuffle](http://en.wikipedia.org/wiki/Fisher–Yates_shuffle).
250
350
  _.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];
255
- shuffled[rand] = value;
256
- });
351
+ var set = isArrayLike(obj) ? obj : _.values(obj);
352
+ var length = set.length;
353
+ var shuffled = Array(length);
354
+ for (var index = 0, rand; index < length; index++) {
355
+ rand = _.random(0, index);
356
+ if (rand !== index) shuffled[index] = shuffled[rand];
357
+ shuffled[rand] = set[index];
358
+ }
257
359
  return shuffled;
258
360
  };
259
361
 
260
- // 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]; };
362
+ // Sample **n** random values from a collection.
363
+ // If **n** is not specified, returns a single random element.
364
+ // The internal `guard` argument allows it to work with `map`.
365
+ _.sample = function(obj, n, guard) {
366
+ if (n == null || guard) {
367
+ if (!isArrayLike(obj)) obj = _.values(obj);
368
+ return obj[_.random(obj.length - 1)];
369
+ }
370
+ return _.shuffle(obj).slice(0, Math.max(0, n));
371
+ };
372
+
373
+ // Sort the object's values by a criterion produced by an iteratee.
374
+ _.sortBy = function(obj, iteratee, context) {
375
+ iteratee = cb(iteratee, context);
263
376
  return _.pluck(_.map(obj, function(value, index, list) {
264
377
  return {
265
- value : value,
266
- criteria : iterator.call(context, value, index, list)
378
+ value: value,
379
+ index: index,
380
+ criteria: iteratee(value, index, list)
267
381
  };
268
382
  }).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;
383
+ var a = left.criteria;
384
+ var b = right.criteria;
385
+ if (a !== b) {
386
+ if (a > b || a === void 0) return 1;
387
+ if (a < b || b === void 0) return -1;
388
+ }
389
+ return left.index - right.index;
273
390
  }), 'value');
274
391
  };
275
392
 
393
+ // An internal function used for aggregate "group by" operations.
394
+ var group = function(behavior) {
395
+ return function(obj, iteratee, context) {
396
+ var result = {};
397
+ iteratee = cb(iteratee, context);
398
+ _.each(obj, function(value, index) {
399
+ var key = iteratee(value, index, obj);
400
+ behavior(result, value, key);
401
+ });
402
+ return result;
403
+ };
404
+ };
405
+
276
406
  // Groups the object's values by a criterion. Pass either a string attribute
277
407
  // 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
- };
408
+ _.groupBy = group(function(result, value, key) {
409
+ if (_.has(result, key)) result[key].push(value); else result[key] = [value];
410
+ });
287
411
 
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);
292
- var low = 0, high = array.length;
293
- while (low < high) {
294
- var mid = (low + high) >> 1;
295
- iterator(array[mid]) < iterator(obj) ? low = mid + 1 : high = mid;
296
- }
297
- return low;
298
- };
412
+ // Indexes the object's values by a criterion, similar to `groupBy`, but for
413
+ // when you know that your index values will be unique.
414
+ _.indexBy = group(function(result, value, key) {
415
+ result[key] = value;
416
+ });
299
417
 
300
- // Safely convert anything iterable into a real, live array.
418
+ // Counts instances of an object that group by a certain criterion. Pass
419
+ // either a string attribute to count by, or a function that returns the
420
+ // criterion.
421
+ _.countBy = group(function(result, value, key) {
422
+ if (_.has(result, key)) result[key]++; else result[key] = 1;
423
+ });
424
+
425
+ // Safely create a real, live array from anything iterable.
301
426
  _.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();
427
+ if (!obj) return [];
428
+ if (_.isArray(obj)) return slice.call(obj);
429
+ if (isArrayLike(obj)) return _.map(obj, _.identity);
306
430
  return _.values(obj);
307
431
  };
308
432
 
309
433
  // Return the number of elements in an object.
310
434
  _.size = function(obj) {
311
- return _.isArray(obj) ? obj.length : _.keys(obj).length;
435
+ if (obj == null) return 0;
436
+ return isArrayLike(obj) ? obj.length : _.keys(obj).length;
437
+ };
438
+
439
+ // Split a collection into two arrays: one whose elements all satisfy the given
440
+ // predicate, and one whose elements all do not satisfy the predicate.
441
+ _.partition = function(obj, predicate, context) {
442
+ predicate = cb(predicate, context);
443
+ var pass = [], fail = [];
444
+ _.each(obj, function(value, key, obj) {
445
+ (predicate(value, key, obj) ? pass : fail).push(value);
446
+ });
447
+ return [pass, fail];
312
448
  };
313
449
 
314
450
  // Array Functions
@@ -318,47 +454,61 @@
318
454
  // values in the array. Aliased as `head` and `take`. The **guard** check
319
455
  // allows it to work with `_.map`.
320
456
  _.first = _.head = _.take = function(array, n, guard) {
321
- return (n != null) && !guard ? slice.call(array, 0, n) : array[0];
457
+ if (array == null) return void 0;
458
+ if (n == null || guard) return array[0];
459
+ return _.initial(array, array.length - n);
322
460
  };
323
461
 
324
- // Returns everything but the last entry of the array. Especcialy useful on
462
+ // Returns everything but the last entry of the array. Especially useful on
325
463
  // the arguments object. Passing **n** will return all the values in
326
- // the array, excluding the last N. The **guard** check allows it to work with
327
- // `_.map`.
464
+ // the array, excluding the last N.
328
465
  _.initial = function(array, n, guard) {
329
- return slice.call(array, 0, array.length - ((n == null) || guard ? 1 : n));
466
+ return slice.call(array, 0, Math.max(0, array.length - (n == null || guard ? 1 : n)));
330
467
  };
331
468
 
332
469
  // Get the last element of an array. Passing **n** will return the last N
333
- // values in the array. The **guard** check allows it to work with `_.map`.
470
+ // values in the array.
334
471
  _.last = function(array, n, guard) {
335
- if ((n != null) && !guard) {
336
- return slice.call(array, Math.max(array.length - n, 0));
337
- } else {
338
- return array[array.length - 1];
339
- }
472
+ if (array == null) return void 0;
473
+ if (n == null || guard) return array[array.length - 1];
474
+ return _.rest(array, Math.max(0, array.length - n));
340
475
  };
341
476
 
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**
345
- // check allows it to work with `_.map`.
346
- _.rest = _.tail = function(array, index, guard) {
347
- return slice.call(array, (index == null) || guard ? 1 : index);
477
+ // Returns everything but the first entry of the array. Aliased as `tail` and `drop`.
478
+ // Especially useful on the arguments object. Passing an **n** will return
479
+ // the rest N values in the array.
480
+ _.rest = _.tail = _.drop = function(array, n, guard) {
481
+ return slice.call(array, n == null || guard ? 1 : n);
348
482
  };
349
483
 
350
484
  // Trim out all falsy values from an array.
351
485
  _.compact = function(array) {
352
- return _.filter(array, function(value){ return !!value; });
486
+ return _.filter(array, _.identity);
487
+ };
488
+
489
+ // Internal implementation of a recursive `flatten` function.
490
+ var flatten = function(input, shallow, strict, startIndex) {
491
+ var output = [], idx = 0;
492
+ for (var i = startIndex || 0, length = getLength(input); i < length; i++) {
493
+ var value = input[i];
494
+ if (isArrayLike(value) && (_.isArray(value) || _.isArguments(value))) {
495
+ //flatten current level of array or arguments object
496
+ if (!shallow) value = flatten(value, shallow, strict);
497
+ var j = 0, len = value.length;
498
+ output.length += len;
499
+ while (j < len) {
500
+ output[idx++] = value[j++];
501
+ }
502
+ } else if (!strict) {
503
+ output[idx++] = value;
504
+ }
505
+ }
506
+ return output;
353
507
  };
354
508
 
355
- // Return a completely flattened version of an array.
509
+ // Flatten out an array, either recursively (by default), or just one level.
356
510
  _.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
- }, []);
511
+ return flatten(array, shallow, false);
362
512
  };
363
513
 
364
514
  // Return a version of the array that does not contain the specified value(s).
@@ -369,99 +519,174 @@
369
519
  // Produce a duplicate-free version of the array. If the array has already
370
520
  // been sorted, you have the option of using a faster algorithm.
371
521
  // Aliased as `unique`.
372
- _.uniq = _.unique = function(array, isSorted, iterator) {
373
- var initial = iterator ? _.map(array, iterator) : array;
374
- 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);
380
- results.push(array[index]);
522
+ _.uniq = _.unique = function(array, isSorted, iteratee, context) {
523
+ if (!_.isBoolean(isSorted)) {
524
+ context = iteratee;
525
+ iteratee = isSorted;
526
+ isSorted = false;
527
+ }
528
+ if (iteratee != null) iteratee = cb(iteratee, context);
529
+ var result = [];
530
+ var seen = [];
531
+ for (var i = 0, length = getLength(array); i < length; i++) {
532
+ var value = array[i],
533
+ computed = iteratee ? iteratee(value, i, array) : value;
534
+ if (isSorted) {
535
+ if (!i || seen !== computed) result.push(value);
536
+ seen = computed;
537
+ } else if (iteratee) {
538
+ if (!_.contains(seen, computed)) {
539
+ seen.push(computed);
540
+ result.push(value);
541
+ }
542
+ } else if (!_.contains(result, value)) {
543
+ result.push(value);
381
544
  }
382
- return memo;
383
- }, []);
384
- return results;
545
+ }
546
+ return result;
385
547
  };
386
548
 
387
549
  // Produce an array that contains the union: each distinct element from all of
388
550
  // the passed-in arrays.
389
551
  _.union = function() {
390
- return _.uniq(_.flatten(arguments, true));
552
+ return _.uniq(flatten(arguments, true, true));
391
553
  };
392
554
 
393
555
  // 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) {
396
- var rest = slice.call(arguments, 1);
397
- return _.filter(_.uniq(array), function(item) {
398
- return _.every(rest, function(other) {
399
- return _.indexOf(other, item) >= 0;
400
- });
401
- });
556
+ // passed-in arrays.
557
+ _.intersection = function(array) {
558
+ var result = [];
559
+ var argsLength = arguments.length;
560
+ for (var i = 0, length = getLength(array); i < length; i++) {
561
+ var item = array[i];
562
+ if (_.contains(result, item)) continue;
563
+ for (var j = 1; j < argsLength; j++) {
564
+ if (!_.contains(arguments[j], item)) break;
565
+ }
566
+ if (j === argsLength) result.push(item);
567
+ }
568
+ return result;
402
569
  };
403
570
 
404
571
  // Take the difference between one array and a number of other arrays.
405
572
  // Only the elements present in just the first array will remain.
406
573
  _.difference = function(array) {
407
- var rest = _.flatten(slice.call(arguments, 1), true);
408
- return _.filter(array, function(value){ return !_.include(rest, value); });
574
+ var rest = flatten(arguments, true, true, 1);
575
+ return _.filter(array, function(value){
576
+ return !_.contains(rest, value);
577
+ });
409
578
  };
410
579
 
411
580
  // Zip together multiple lists into a single array -- elements that share
412
581
  // an index go together.
413
582
  _.zip = function() {
414
- var args = slice.call(arguments);
415
- var length = _.max(_.pluck(args, 'length'));
416
- var results = new Array(length);
417
- for (var i = 0; i < length; i++) results[i] = _.pluck(args, "" + i);
418
- return results;
583
+ return _.unzip(arguments);
419
584
  };
420
585
 
421
- // If the browser doesn't supply us with indexOf (I'm looking at you, **MSIE**),
422
- // we need this function. Return the position of the first occurrence of an
423
- // item in an array, or -1 if the item is not included in the array.
424
- // Delegates to **ECMAScript 5**'s native `indexOf` if available.
425
- // If the array is large and already in sort order, pass `true`
426
- // for **isSorted** to use binary search.
427
- _.indexOf = function(array, item, isSorted) {
428
- if (array == null) return -1;
429
- var i, l;
430
- if (isSorted) {
431
- i = _.sortedIndex(array, item);
432
- return array[i] === item ? i : -1;
586
+ // Complement of _.zip. Unzip accepts an array of arrays and groups
587
+ // each array's elements on shared indices
588
+ _.unzip = function(array) {
589
+ var length = array && _.max(array, getLength).length || 0;
590
+ var result = Array(length);
591
+
592
+ for (var index = 0; index < length; index++) {
593
+ result[index] = _.pluck(array, index);
433
594
  }
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;
436
- return -1;
595
+ return result;
437
596
  };
438
597
 
439
- // Delegates to **ECMAScript 5**'s native `lastIndexOf` if available.
440
- _.lastIndexOf = function(array, item) {
441
- 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;
445
- return -1;
598
+ // Converts lists into objects. Pass either a single array of `[key, value]`
599
+ // pairs, or two parallel arrays of the same length -- one of keys, and one of
600
+ // the corresponding values.
601
+ _.object = function(list, values) {
602
+ var result = {};
603
+ for (var i = 0, length = getLength(list); i < length; i++) {
604
+ if (values) {
605
+ result[list[i]] = values[i];
606
+ } else {
607
+ result[list[i][0]] = list[i][1];
608
+ }
609
+ }
610
+ return result;
446
611
  };
447
612
 
613
+ // Generator function to create the findIndex and findLastIndex functions
614
+ function createPredicateIndexFinder(dir) {
615
+ return function(array, predicate, context) {
616
+ predicate = cb(predicate, context);
617
+ var length = getLength(array);
618
+ var index = dir > 0 ? 0 : length - 1;
619
+ for (; index >= 0 && index < length; index += dir) {
620
+ if (predicate(array[index], index, array)) return index;
621
+ }
622
+ return -1;
623
+ };
624
+ }
625
+
626
+ // Returns the first index on an array-like that passes a predicate test
627
+ _.findIndex = createPredicateIndexFinder(1);
628
+ _.findLastIndex = createPredicateIndexFinder(-1);
629
+
630
+ // Use a comparator function to figure out the smallest index at which
631
+ // an object should be inserted so as to maintain order. Uses binary search.
632
+ _.sortedIndex = function(array, obj, iteratee, context) {
633
+ iteratee = cb(iteratee, context, 1);
634
+ var value = iteratee(obj);
635
+ var low = 0, high = getLength(array);
636
+ while (low < high) {
637
+ var mid = Math.floor((low + high) / 2);
638
+ if (iteratee(array[mid]) < value) low = mid + 1; else high = mid;
639
+ }
640
+ return low;
641
+ };
642
+
643
+ // Generator function to create the indexOf and lastIndexOf functions
644
+ function createIndexFinder(dir, predicateFind, sortedIndex) {
645
+ return function(array, item, idx) {
646
+ var i = 0, length = getLength(array);
647
+ if (typeof idx == 'number') {
648
+ if (dir > 0) {
649
+ i = idx >= 0 ? idx : Math.max(idx + length, i);
650
+ } else {
651
+ length = idx >= 0 ? Math.min(idx + 1, length) : idx + length + 1;
652
+ }
653
+ } else if (sortedIndex && idx && length) {
654
+ idx = sortedIndex(array, item);
655
+ return array[idx] === item ? idx : -1;
656
+ }
657
+ if (item !== item) {
658
+ idx = predicateFind(slice.call(array, i, length), _.isNaN);
659
+ return idx >= 0 ? idx + i : -1;
660
+ }
661
+ for (idx = dir > 0 ? i : length - 1; idx >= 0 && idx < length; idx += dir) {
662
+ if (array[idx] === item) return idx;
663
+ }
664
+ return -1;
665
+ };
666
+ }
667
+
668
+ // Return the position of the first occurrence of an item in an array,
669
+ // or -1 if the item is not included in the array.
670
+ // If the array is large and already in sort order, pass `true`
671
+ // for **isSorted** to use binary search.
672
+ _.indexOf = createIndexFinder(1, _.findIndex, _.sortedIndex);
673
+ _.lastIndexOf = createIndexFinder(-1, _.findLastIndex);
674
+
448
675
  // Generate an integer Array containing an arithmetic progression. A port of
449
676
  // the native Python `range()` function. See
450
677
  // [the Python documentation](http://docs.python.org/library/functions.html#range).
451
678
  _.range = function(start, stop, step) {
452
- if (arguments.length <= 1) {
679
+ if (stop == null) {
453
680
  stop = start || 0;
454
681
  start = 0;
455
682
  }
456
- step = arguments[2] || 1;
683
+ step = step || 1;
457
684
 
458
- var len = Math.max(Math.ceil((stop - start) / step), 0);
459
- var idx = 0;
460
- var range = new Array(len);
685
+ var length = Math.max(Math.ceil((stop - start) / step), 0);
686
+ var range = Array(length);
461
687
 
462
- while(idx < len) {
463
- range[idx++] = start;
464
- start += step;
688
+ for (var idx = 0; idx < length; idx++, start += step) {
689
+ range[idx] = start;
465
690
  }
466
691
 
467
692
  return range;
@@ -470,80 +695,117 @@
470
695
  // Function (ahem) Functions
471
696
  // ------------------
472
697
 
473
- // Reusable constructor function for prototype setting.
474
- var ctor = function(){};
698
+ // Determines whether to execute a function as a constructor
699
+ // or a normal function with the provided arguments
700
+ var executeBound = function(sourceFunc, boundFunc, context, callingContext, args) {
701
+ if (!(callingContext instanceof boundFunc)) return sourceFunc.apply(context, args);
702
+ var self = baseCreate(sourceFunc.prototype);
703
+ var result = sourceFunc.apply(self, args);
704
+ if (_.isObject(result)) return result;
705
+ return self;
706
+ };
475
707
 
476
708
  // 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));
483
- if (!_.isFunction(func)) throw new TypeError;
484
- args = slice.call(arguments, 2);
485
- return bound = function() {
486
- if (!(this instanceof bound)) return func.apply(context, args.concat(slice.call(arguments)));
487
- ctor.prototype = func.prototype;
488
- var self = new ctor;
489
- var result = func.apply(self, args.concat(slice.call(arguments)));
490
- if (Object(result) === result) return result;
491
- return self;
709
+ // optionally). Delegates to **ECMAScript 5**'s native `Function.bind` if
710
+ // available.
711
+ _.bind = function(func, context) {
712
+ if (nativeBind && func.bind === nativeBind) return nativeBind.apply(func, slice.call(arguments, 1));
713
+ if (!_.isFunction(func)) throw new TypeError('Bind must be called on a function');
714
+ var args = slice.call(arguments, 2);
715
+ var bound = function() {
716
+ return executeBound(func, bound, context, this, args.concat(slice.call(arguments)));
717
+ };
718
+ return bound;
719
+ };
720
+
721
+ // Partially apply a function by creating a version that has had some of its
722
+ // arguments pre-filled, without changing its dynamic `this` context. _ acts
723
+ // as a placeholder, allowing any combination of arguments to be pre-filled.
724
+ _.partial = function(func) {
725
+ var boundArgs = slice.call(arguments, 1);
726
+ var bound = function() {
727
+ var position = 0, length = boundArgs.length;
728
+ var args = Array(length);
729
+ for (var i = 0; i < length; i++) {
730
+ args[i] = boundArgs[i] === _ ? arguments[position++] : boundArgs[i];
731
+ }
732
+ while (position < arguments.length) args.push(arguments[position++]);
733
+ return executeBound(func, bound, this, this, args);
492
734
  };
735
+ return bound;
493
736
  };
494
737
 
495
- // Bind all of an object's methods to that object. Useful for ensuring that
496
- // all callbacks defined on an object belong to it.
738
+ // Bind a number of an object's methods to that object. Remaining arguments
739
+ // are the method names to be bound. Useful for ensuring that all callbacks
740
+ // defined on an object belong to it.
497
741
  _.bindAll = function(obj) {
498
- var funcs = slice.call(arguments, 1);
499
- if (funcs.length == 0) funcs = _.functions(obj);
500
- each(funcs, function(f) { obj[f] = _.bind(obj[f], obj); });
742
+ var i, length = arguments.length, key;
743
+ if (length <= 1) throw new Error('bindAll must be passed function names');
744
+ for (i = 1; i < length; i++) {
745
+ key = arguments[i];
746
+ obj[key] = _.bind(obj[key], obj);
747
+ }
501
748
  return obj;
502
749
  };
503
750
 
504
751
  // Memoize an expensive function by storing its results.
505
752
  _.memoize = function(func, hasher) {
506
- var memo = {};
507
- hasher || (hasher = _.identity);
508
- return function() {
509
- var key = hasher.apply(this, arguments);
510
- return _.has(memo, key) ? memo[key] : (memo[key] = func.apply(this, arguments));
753
+ var memoize = function(key) {
754
+ var cache = memoize.cache;
755
+ var address = '' + (hasher ? hasher.apply(this, arguments) : key);
756
+ if (!_.has(cache, address)) cache[address] = func.apply(this, arguments);
757
+ return cache[address];
511
758
  };
759
+ memoize.cache = {};
760
+ return memoize;
512
761
  };
513
762
 
514
763
  // Delays a function for the given number of milliseconds, and then calls
515
764
  // it with the arguments supplied.
516
765
  _.delay = function(func, wait) {
517
766
  var args = slice.call(arguments, 2);
518
- return setTimeout(function(){ return func.apply(null, args); }, wait);
767
+ return setTimeout(function(){
768
+ return func.apply(null, args);
769
+ }, wait);
519
770
  };
520
771
 
521
772
  // Defers a function, scheduling it to run after the current call stack has
522
773
  // cleared.
523
- _.defer = function(func) {
524
- return _.delay.apply(_, [func, 1].concat(slice.call(arguments, 1)));
525
- };
774
+ _.defer = _.partial(_.delay, _, 1);
526
775
 
527
776
  // 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);
777
+ // during a given window of time. Normally, the throttled function will run
778
+ // as much as it can, without ever going more than once per `wait` duration;
779
+ // but if you'd like to disable the execution on the leading edge, pass
780
+ // `{leading: false}`. To disable execution on the trailing edge, ditto.
781
+ _.throttle = function(func, wait, options) {
782
+ var context, args, result;
783
+ var timeout = null;
784
+ var previous = 0;
785
+ if (!options) options = {};
786
+ var later = function() {
787
+ previous = options.leading === false ? 0 : _.now();
788
+ timeout = null;
789
+ result = func.apply(context, args);
790
+ if (!timeout) context = args = null;
791
+ };
532
792
  return function() {
533
- context = this; args = arguments;
534
- var later = function() {
535
- 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 {
793
+ var now = _.now();
794
+ if (!previous && options.leading === false) previous = now;
795
+ var remaining = wait - (now - previous);
796
+ context = this;
797
+ args = arguments;
798
+ if (remaining <= 0 || remaining > wait) {
799
+ if (timeout) {
800
+ clearTimeout(timeout);
801
+ timeout = null;
802
+ }
803
+ previous = now;
543
804
  result = func.apply(context, args);
805
+ if (!timeout) context = args = null;
806
+ } else if (!timeout && options.trailing !== false) {
807
+ timeout = setTimeout(later, remaining);
544
808
  }
545
- whenDone();
546
- throttling = true;
547
809
  return result;
548
810
  };
549
811
  };
@@ -553,27 +815,34 @@
553
815
  // N milliseconds. If `immediate` is passed, trigger the function on the
554
816
  // leading edge, instead of the trailing.
555
817
  _.debounce = function(func, wait, immediate) {
556
- var timeout;
557
- return function() {
558
- var context = this, args = arguments;
559
- var later = function() {
818
+ var timeout, args, context, timestamp, result;
819
+
820
+ var later = function() {
821
+ var last = _.now() - timestamp;
822
+
823
+ if (last < wait && last >= 0) {
824
+ timeout = setTimeout(later, wait - last);
825
+ } else {
560
826
  timeout = null;
561
- if (!immediate) func.apply(context, args);
562
- };
563
- if (immediate && !timeout) func.apply(context, args);
564
- clearTimeout(timeout);
565
- timeout = setTimeout(later, wait);
827
+ if (!immediate) {
828
+ result = func.apply(context, args);
829
+ if (!timeout) context = args = null;
830
+ }
831
+ }
566
832
  };
567
- };
568
833
 
569
- // Returns a function that will be executed at most one time, no matter how
570
- // often you call it. Useful for lazy initialization.
571
- _.once = function(func) {
572
- var ran = false, memo;
573
834
  return function() {
574
- if (ran) return memo;
575
- ran = true;
576
- return memo = func.apply(this, arguments);
835
+ context = this;
836
+ args = arguments;
837
+ timestamp = _.now();
838
+ var callNow = immediate && !timeout;
839
+ if (!timeout) timeout = setTimeout(later, wait);
840
+ if (callNow) {
841
+ result = func.apply(context, args);
842
+ context = args = null;
843
+ }
844
+
845
+ return result;
577
846
  };
578
847
  };
579
848
 
@@ -581,48 +850,146 @@
581
850
  // allowing you to adjust arguments, run code before and after, and
582
851
  // conditionally execute the original function.
583
852
  _.wrap = function(func, wrapper) {
853
+ return _.partial(wrapper, func);
854
+ };
855
+
856
+ // Returns a negated version of the passed-in predicate.
857
+ _.negate = function(predicate) {
584
858
  return function() {
585
- var args = [func].concat(slice.call(arguments, 0));
586
- return wrapper.apply(this, args);
859
+ return !predicate.apply(this, arguments);
587
860
  };
588
861
  };
589
862
 
590
863
  // Returns a function that is the composition of a list of functions, each
591
864
  // consuming the return value of the function that follows.
592
865
  _.compose = function() {
593
- var funcs = arguments;
866
+ var args = arguments;
867
+ var start = args.length - 1;
594
868
  return function() {
595
- var args = arguments;
596
- for (var i = funcs.length - 1; i >= 0; i--) {
597
- args = [funcs[i].apply(this, args)];
598
- }
599
- return args[0];
869
+ var i = start;
870
+ var result = args[start].apply(this, arguments);
871
+ while (i--) result = args[i].call(this, result);
872
+ return result;
600
873
  };
601
874
  };
602
875
 
603
- // Returns a function that will only be executed after being called N times.
876
+ // Returns a function that will only be executed on and after the Nth call.
604
877
  _.after = function(times, func) {
605
- if (times <= 0) return func();
606
878
  return function() {
607
- if (--times < 1) { return func.apply(this, arguments); }
879
+ if (--times < 1) {
880
+ return func.apply(this, arguments);
881
+ }
882
+ };
883
+ };
884
+
885
+ // Returns a function that will only be executed up to (but not including) the Nth call.
886
+ _.before = function(times, func) {
887
+ var memo;
888
+ return function() {
889
+ if (--times > 0) {
890
+ memo = func.apply(this, arguments);
891
+ }
892
+ if (times <= 1) func = null;
893
+ return memo;
608
894
  };
609
895
  };
610
896
 
897
+ // Returns a function that will be executed at most one time, no matter how
898
+ // often you call it. Useful for lazy initialization.
899
+ _.once = _.partial(_.before, 2);
900
+
611
901
  // Object Functions
612
902
  // ----------------
613
903
 
614
- // Retrieve the names of an object's properties.
904
+ // Keys in IE < 9 that won't be iterated by `for key in ...` and thus missed.
905
+ var hasEnumBug = !{toString: null}.propertyIsEnumerable('toString');
906
+ var nonEnumerableProps = ['valueOf', 'isPrototypeOf', 'toString',
907
+ 'propertyIsEnumerable', 'hasOwnProperty', 'toLocaleString'];
908
+
909
+ function collectNonEnumProps(obj, keys) {
910
+ var nonEnumIdx = nonEnumerableProps.length;
911
+ var constructor = obj.constructor;
912
+ var proto = (_.isFunction(constructor) && constructor.prototype) || ObjProto;
913
+
914
+ // Constructor is a special case.
915
+ var prop = 'constructor';
916
+ if (_.has(obj, prop) && !_.contains(keys, prop)) keys.push(prop);
917
+
918
+ while (nonEnumIdx--) {
919
+ prop = nonEnumerableProps[nonEnumIdx];
920
+ if (prop in obj && obj[prop] !== proto[prop] && !_.contains(keys, prop)) {
921
+ keys.push(prop);
922
+ }
923
+ }
924
+ }
925
+
926
+ // Retrieve the names of an object's own properties.
615
927
  // Delegates to **ECMAScript 5**'s native `Object.keys`
616
- _.keys = nativeKeys || function(obj) {
617
- if (obj !== Object(obj)) throw new TypeError('Invalid object');
928
+ _.keys = function(obj) {
929
+ if (!_.isObject(obj)) return [];
930
+ if (nativeKeys) return nativeKeys(obj);
931
+ var keys = [];
932
+ for (var key in obj) if (_.has(obj, key)) keys.push(key);
933
+ // Ahem, IE < 9.
934
+ if (hasEnumBug) collectNonEnumProps(obj, keys);
935
+ return keys;
936
+ };
937
+
938
+ // Retrieve all the property names of an object.
939
+ _.allKeys = function(obj) {
940
+ if (!_.isObject(obj)) return [];
618
941
  var keys = [];
619
- for (var key in obj) if (_.has(obj, key)) keys[keys.length] = key;
942
+ for (var key in obj) keys.push(key);
943
+ // Ahem, IE < 9.
944
+ if (hasEnumBug) collectNonEnumProps(obj, keys);
620
945
  return keys;
621
946
  };
622
947
 
623
948
  // Retrieve the values of an object's properties.
624
949
  _.values = function(obj) {
625
- return _.map(obj, _.identity);
950
+ var keys = _.keys(obj);
951
+ var length = keys.length;
952
+ var values = Array(length);
953
+ for (var i = 0; i < length; i++) {
954
+ values[i] = obj[keys[i]];
955
+ }
956
+ return values;
957
+ };
958
+
959
+ // Returns the results of applying the iteratee to each element of the object
960
+ // In contrast to _.map it returns an object
961
+ _.mapObject = function(obj, iteratee, context) {
962
+ iteratee = cb(iteratee, context);
963
+ var keys = _.keys(obj),
964
+ length = keys.length,
965
+ results = {},
966
+ currentKey;
967
+ for (var index = 0; index < length; index++) {
968
+ currentKey = keys[index];
969
+ results[currentKey] = iteratee(obj[currentKey], currentKey, obj);
970
+ }
971
+ return results;
972
+ };
973
+
974
+ // Convert an object into a list of `[key, value]` pairs.
975
+ _.pairs = function(obj) {
976
+ var keys = _.keys(obj);
977
+ var length = keys.length;
978
+ var pairs = Array(length);
979
+ for (var i = 0; i < length; i++) {
980
+ pairs[i] = [keys[i], obj[keys[i]]];
981
+ }
982
+ return pairs;
983
+ };
984
+
985
+ // Invert the keys and values of an object. The values must be serializable.
986
+ _.invert = function(obj) {
987
+ var result = {};
988
+ var keys = _.keys(obj);
989
+ for (var i = 0, length = keys.length; i < length; i++) {
990
+ result[obj[keys[i]]] = keys[i];
991
+ }
992
+ return result;
626
993
  };
627
994
 
628
995
  // Return a sorted list of the function names available on the object.
@@ -636,32 +1003,65 @@
636
1003
  };
637
1004
 
638
1005
  // Extend a given object with all the properties in passed-in object(s).
639
- _.extend = function(obj) {
640
- each(slice.call(arguments, 1), function(source) {
641
- for (var prop in source) {
642
- obj[prop] = source[prop];
643
- }
644
- });
645
- return obj;
1006
+ _.extend = createAssigner(_.allKeys);
1007
+
1008
+ // Assigns a given object with all the own properties in the passed-in object(s)
1009
+ // (https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Object/assign)
1010
+ _.extendOwn = _.assign = createAssigner(_.keys);
1011
+
1012
+ // Returns the first key on an object that passes a predicate test
1013
+ _.findKey = function(obj, predicate, context) {
1014
+ predicate = cb(predicate, context);
1015
+ var keys = _.keys(obj), key;
1016
+ for (var i = 0, length = keys.length; i < length; i++) {
1017
+ key = keys[i];
1018
+ if (predicate(obj[key], key, obj)) return key;
1019
+ }
646
1020
  };
647
1021
 
648
1022
  // Return a copy of the object only containing the whitelisted properties.
649
- _.pick = function(obj) {
650
- var result = {};
651
- each(_.flatten(slice.call(arguments, 1)), function(key) {
652
- if (key in obj) result[key] = obj[key];
653
- });
1023
+ _.pick = function(object, oiteratee, context) {
1024
+ var result = {}, obj = object, iteratee, keys;
1025
+ if (obj == null) return result;
1026
+ if (_.isFunction(oiteratee)) {
1027
+ keys = _.allKeys(obj);
1028
+ iteratee = optimizeCb(oiteratee, context);
1029
+ } else {
1030
+ keys = flatten(arguments, false, false, 1);
1031
+ iteratee = function(value, key, obj) { return key in obj; };
1032
+ obj = Object(obj);
1033
+ }
1034
+ for (var i = 0, length = keys.length; i < length; i++) {
1035
+ var key = keys[i];
1036
+ var value = obj[key];
1037
+ if (iteratee(value, key, obj)) result[key] = value;
1038
+ }
654
1039
  return result;
655
1040
  };
656
1041
 
1042
+ // Return a copy of the object without the blacklisted properties.
1043
+ _.omit = function(obj, iteratee, context) {
1044
+ if (_.isFunction(iteratee)) {
1045
+ iteratee = _.negate(iteratee);
1046
+ } else {
1047
+ var keys = _.map(flatten(arguments, false, false, 1), String);
1048
+ iteratee = function(value, key) {
1049
+ return !_.contains(keys, key);
1050
+ };
1051
+ }
1052
+ return _.pick(obj, iteratee, context);
1053
+ };
1054
+
657
1055
  // Fill in a given object with default properties.
658
- _.defaults = function(obj) {
659
- each(slice.call(arguments, 1), function(source) {
660
- for (var prop in source) {
661
- if (obj[prop] == null) obj[prop] = source[prop];
662
- }
663
- });
664
- return obj;
1056
+ _.defaults = createAssigner(_.allKeys, true);
1057
+
1058
+ // Creates an object that inherits from the given prototype object.
1059
+ // If additional properties are provided then they will be added to the
1060
+ // created object.
1061
+ _.create = function(prototype, props) {
1062
+ var result = baseCreate(prototype);
1063
+ if (props) _.extendOwn(result, props);
1064
+ return result;
665
1065
  };
666
1066
 
667
1067
  // Create a (shallow-cloned) duplicate of an object.
@@ -678,173 +1078,178 @@
678
1078
  return obj;
679
1079
  };
680
1080
 
681
- // Internal recursive comparison function.
682
- function eq(a, b, stack) {
1081
+ // Returns whether an object has a given set of `key:value` pairs.
1082
+ _.isMatch = function(object, attrs) {
1083
+ var keys = _.keys(attrs), length = keys.length;
1084
+ if (object == null) return !length;
1085
+ var obj = Object(object);
1086
+ for (var i = 0; i < length; i++) {
1087
+ var key = keys[i];
1088
+ if (attrs[key] !== obj[key] || !(key in obj)) return false;
1089
+ }
1090
+ return true;
1091
+ };
1092
+
1093
+
1094
+ // Internal recursive comparison function for `isEqual`.
1095
+ var eq = function(a, b, aStack, bStack) {
683
1096
  // 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.
685
- if (a === b) return a !== 0 || 1 / a == 1 / b;
1097
+ // See the [Harmony `egal` proposal](http://wiki.ecmascript.org/doku.php?id=harmony:egal).
1098
+ if (a === b) return a !== 0 || 1 / a === 1 / b;
686
1099
  // A strict comparison is necessary because `null == undefined`.
687
1100
  if (a == null || b == null) return a === b;
688
1101
  // 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);
1102
+ if (a instanceof _) a = a._wrapped;
1103
+ if (b instanceof _) b = b._wrapped;
694
1104
  // Compare `[[Class]]` names.
695
1105
  var className = toString.call(a);
696
- if (className != toString.call(b)) return false;
1106
+ if (className !== toString.call(b)) return false;
697
1107
  switch (className) {
698
- // Strings, numbers, dates, and booleans are compared by value.
1108
+ // Strings, numbers, regular expressions, dates, and booleans are compared by value.
1109
+ case '[object RegExp]':
1110
+ // RegExps are coerced to strings for comparison (Note: '' + /a/i === '/a/i')
699
1111
  case '[object String]':
700
1112
  // Primitives and their corresponding object wrappers are equivalent; thus, `"5"` is
701
1113
  // equivalent to `new String("5")`.
702
- return a == String(b);
1114
+ return '' + a === '' + b;
703
1115
  case '[object Number]':
704
- // `NaN`s are equivalent, but non-reflexive. An `egal` comparison is performed for
705
- // other numeric values.
706
- return a != +a ? b != +b : (a == 0 ? 1 / a == 1 / b : a == +b);
1116
+ // `NaN`s are equivalent, but non-reflexive.
1117
+ // Object(NaN) is equivalent to NaN
1118
+ if (+a !== +a) return +b !== +b;
1119
+ // An `egal` comparison is performed for other numeric values.
1120
+ return +a === 0 ? 1 / +a === 1 / b : +a === +b;
707
1121
  case '[object Date]':
708
1122
  case '[object Boolean]':
709
1123
  // Coerce dates and booleans to numeric primitive values. Dates are compared by their
710
1124
  // millisecond representations. Note that invalid dates with millisecond representations
711
1125
  // of `NaN` are not equivalent.
712
- return +a == +b;
713
- // RegExps are compared by their source patterns and flags.
714
- case '[object RegExp]':
715
- return a.source == b.source &&
716
- a.global == b.global &&
717
- a.multiline == b.multiline &&
718
- a.ignoreCase == b.ignoreCase;
1126
+ return +a === +b;
1127
+ }
1128
+
1129
+ var areArrays = className === '[object Array]';
1130
+ if (!areArrays) {
1131
+ if (typeof a != 'object' || typeof b != 'object') return false;
1132
+
1133
+ // Objects with different constructors are not equivalent, but `Object`s or `Array`s
1134
+ // from different frames are.
1135
+ var aCtor = a.constructor, bCtor = b.constructor;
1136
+ if (aCtor !== bCtor && !(_.isFunction(aCtor) && aCtor instanceof aCtor &&
1137
+ _.isFunction(bCtor) && bCtor instanceof bCtor)
1138
+ && ('constructor' in a && 'constructor' in b)) {
1139
+ return false;
1140
+ }
719
1141
  }
720
- if (typeof a != 'object' || typeof b != 'object') return false;
721
1142
  // Assume equality for cyclic structures. The algorithm for detecting cyclic
722
1143
  // structures is adapted from ES 5.1 section 15.12.3, abstract operation `JO`.
723
- var length = stack.length;
1144
+
1145
+ // Initializing stack of traversed objects.
1146
+ // It's done here since we only need them for objects and arrays comparison.
1147
+ aStack = aStack || [];
1148
+ bStack = bStack || [];
1149
+ var length = aStack.length;
724
1150
  while (length--) {
725
1151
  // Linear search. Performance is inversely proportional to the number of
726
1152
  // unique nested structures.
727
- if (stack[length] == a) return true;
1153
+ if (aStack[length] === a) return bStack[length] === b;
728
1154
  }
1155
+
729
1156
  // Add the first object to the stack of traversed objects.
730
- stack.push(a);
731
- var size = 0, result = true;
1157
+ aStack.push(a);
1158
+ bStack.push(b);
1159
+
732
1160
  // Recursively compare objects and arrays.
733
- if (className == '[object Array]') {
1161
+ if (areArrays) {
734
1162
  // Compare array lengths to determine if a deep comparison is necessary.
735
- size = a.length;
736
- result = size == b.length;
737
- if (result) {
738
- // Deep compare the contents, ignoring non-numeric properties.
739
- 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;
742
- }
1163
+ length = a.length;
1164
+ if (length !== b.length) return false;
1165
+ // Deep compare the contents, ignoring non-numeric properties.
1166
+ while (length--) {
1167
+ if (!eq(a[length], b[length], aStack, bStack)) return false;
743
1168
  }
744
1169
  } else {
745
- // Objects with different constructors are not equivalent.
746
- if ('constructor' in a != 'constructor' in b || a.constructor != b.constructor) return false;
747
1170
  // Deep compare objects.
748
- for (var key in a) {
749
- if (_.has(a, key)) {
750
- // Count the expected number of properties.
751
- size++;
752
- // Deep compare each member.
753
- if (!(result = _.has(b, key) && eq(a[key], b[key], stack))) break;
754
- }
755
- }
756
- // Ensure that both objects contain the same number of properties.
757
- if (result) {
758
- for (key in b) {
759
- if (_.has(b, key) && !(size--)) break;
760
- }
761
- result = !size;
1171
+ var keys = _.keys(a), key;
1172
+ length = keys.length;
1173
+ // Ensure that both objects contain the same number of properties before comparing deep equality.
1174
+ if (_.keys(b).length !== length) return false;
1175
+ while (length--) {
1176
+ // Deep compare each member
1177
+ key = keys[length];
1178
+ if (!(_.has(b, key) && eq(a[key], b[key], aStack, bStack))) return false;
762
1179
  }
763
1180
  }
764
1181
  // Remove the first object from the stack of traversed objects.
765
- stack.pop();
766
- return result;
767
- }
1182
+ aStack.pop();
1183
+ bStack.pop();
1184
+ return true;
1185
+ };
768
1186
 
769
1187
  // Perform a deep comparison to check if two objects are equal.
770
1188
  _.isEqual = function(a, b) {
771
- return eq(a, b, []);
1189
+ return eq(a, b);
772
1190
  };
773
1191
 
774
1192
  // Is a given array, string, or object empty?
775
1193
  // An "empty" object has no enumerable own-properties.
776
1194
  _.isEmpty = function(obj) {
777
1195
  if (obj == null) return true;
778
- if (_.isArray(obj) || _.isString(obj)) return obj.length === 0;
779
- for (var key in obj) if (_.has(obj, key)) return false;
780
- return true;
1196
+ if (isArrayLike(obj) && (_.isArray(obj) || _.isString(obj) || _.isArguments(obj))) return obj.length === 0;
1197
+ return _.keys(obj).length === 0;
781
1198
  };
782
1199
 
783
1200
  // Is a given value a DOM element?
784
1201
  _.isElement = function(obj) {
785
- return !!(obj && obj.nodeType == 1);
1202
+ return !!(obj && obj.nodeType === 1);
786
1203
  };
787
1204
 
788
1205
  // Is a given value an array?
789
1206
  // Delegates to ECMA5's native Array.isArray
790
1207
  _.isArray = nativeIsArray || function(obj) {
791
- return toString.call(obj) == '[object Array]';
1208
+ return toString.call(obj) === '[object Array]';
792
1209
  };
793
1210
 
794
1211
  // Is a given variable an object?
795
1212
  _.isObject = function(obj) {
796
- return obj === Object(obj);
1213
+ var type = typeof obj;
1214
+ return type === 'function' || type === 'object' && !!obj;
797
1215
  };
798
1216
 
799
- // Is a given variable an arguments object?
800
- _.isArguments = function(obj) {
801
- return toString.call(obj) == '[object Arguments]';
802
- };
1217
+ // Add some isType methods: isArguments, isFunction, isString, isNumber, isDate, isRegExp, isError.
1218
+ _.each(['Arguments', 'Function', 'String', 'Number', 'Date', 'RegExp', 'Error'], function(name) {
1219
+ _['is' + name] = function(obj) {
1220
+ return toString.call(obj) === '[object ' + name + ']';
1221
+ };
1222
+ });
1223
+
1224
+ // Define a fallback version of the method in browsers (ahem, IE < 9), where
1225
+ // there isn't any inspectable "Arguments" type.
803
1226
  if (!_.isArguments(arguments)) {
804
1227
  _.isArguments = function(obj) {
805
- return !!(obj && _.has(obj, 'callee'));
1228
+ return _.has(obj, 'callee');
806
1229
  };
807
1230
  }
808
1231
 
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
- };
1232
+ // Optimize `isFunction` if appropriate. Work around some typeof bugs in old v8,
1233
+ // IE 11 (#1621), and in Safari 8 (#1929).
1234
+ if (typeof /./ != 'function' && typeof Int8Array != 'object') {
1235
+ _.isFunction = function(obj) {
1236
+ return typeof obj == 'function' || false;
1237
+ };
1238
+ }
823
1239
 
824
1240
  // Is a given object a finite number?
825
1241
  _.isFinite = function(obj) {
826
- return _.isNumber(obj) && isFinite(obj);
1242
+ return isFinite(obj) && !isNaN(parseFloat(obj));
827
1243
  };
828
1244
 
829
- // Is the given value `NaN`?
1245
+ // Is the given value `NaN`? (NaN is the only number which does not equal itself).
830
1246
  _.isNaN = function(obj) {
831
- // `NaN` is the only value for which `===` is not reflexive.
832
- return obj !== obj;
1247
+ return _.isNumber(obj) && obj !== +obj;
833
1248
  };
834
1249
 
835
1250
  // Is a given value a boolean?
836
1251
  _.isBoolean = function(obj) {
837
- return obj === true || obj === false || toString.call(obj) == '[object Boolean]';
838
- };
839
-
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]';
1252
+ return obj === true || obj === false || toString.call(obj) === '[object Boolean]';
848
1253
  };
849
1254
 
850
1255
  // Is a given value equal to null?
@@ -857,9 +1262,10 @@
857
1262
  return obj === void 0;
858
1263
  };
859
1264
 
860
- // Has own property?
1265
+ // Shortcut function for checking if an object has a given property directly
1266
+ // on itself (in other words, not on a prototype).
861
1267
  _.has = function(obj, key) {
862
- return hasOwnProperty.call(obj, key);
1268
+ return obj != null && hasOwnProperty.call(obj, key);
863
1269
  };
864
1270
 
865
1271
  // Utility Functions
@@ -872,42 +1278,103 @@
872
1278
  return this;
873
1279
  };
874
1280
 
875
- // Keep the identity function around for default iterators.
1281
+ // Keep the identity function around for default iteratees.
876
1282
  _.identity = function(value) {
877
1283
  return value;
878
1284
  };
879
1285
 
1286
+ // Predicate-generating functions. Often useful outside of Underscore.
1287
+ _.constant = function(value) {
1288
+ return function() {
1289
+ return value;
1290
+ };
1291
+ };
1292
+
1293
+ _.noop = function(){};
1294
+
1295
+ _.property = property;
1296
+
1297
+ // Generates a function for a given object that returns a given property.
1298
+ _.propertyOf = function(obj) {
1299
+ return obj == null ? function(){} : function(key) {
1300
+ return obj[key];
1301
+ };
1302
+ };
1303
+
1304
+ // Returns a predicate for checking whether an object has a given set of
1305
+ // `key:value` pairs.
1306
+ _.matcher = _.matches = function(attrs) {
1307
+ attrs = _.extendOwn({}, attrs);
1308
+ return function(obj) {
1309
+ return _.isMatch(obj, attrs);
1310
+ };
1311
+ };
1312
+
880
1313
  // Run a function **n** times.
881
- _.times = function (n, iterator, context) {
882
- for (var i = 0; i < n; i++) iterator.call(context, i);
1314
+ _.times = function(n, iteratee, context) {
1315
+ var accum = Array(Math.max(0, n));
1316
+ iteratee = optimizeCb(iteratee, context, 1);
1317
+ for (var i = 0; i < n; i++) accum[i] = iteratee(i);
1318
+ return accum;
1319
+ };
1320
+
1321
+ // Return a random integer between min and max (inclusive).
1322
+ _.random = function(min, max) {
1323
+ if (max == null) {
1324
+ max = min;
1325
+ min = 0;
1326
+ }
1327
+ return min + Math.floor(Math.random() * (max - min + 1));
883
1328
  };
884
1329
 
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;');
1330
+ // A (possibly faster) way to get the current timestamp as an integer.
1331
+ _.now = Date.now || function() {
1332
+ return new Date().getTime();
888
1333
  };
889
1334
 
890
- // If the value of the named property is a function then invoke it;
891
- // otherwise, return it.
892
- _.result = function(object, property) {
893
- if (object == null) return null;
894
- var value = object[property];
895
- return _.isFunction(value) ? value.call(object) : value;
1335
+ // List of HTML entities for escaping.
1336
+ var escapeMap = {
1337
+ '&': '&amp;',
1338
+ '<': '&lt;',
1339
+ '>': '&gt;',
1340
+ '"': '&quot;',
1341
+ "'": '&#x27;',
1342
+ '`': '&#x60;'
896
1343
  };
1344
+ var unescapeMap = _.invert(escapeMap);
897
1345
 
898
- // Add your own custom functions to the Underscore object, ensuring that
899
- // they're correctly added to the OOP wrapper as well.
900
- _.mixin = function(obj) {
901
- each(_.functions(obj), function(name){
902
- addToWrapper(name, _[name] = obj[name]);
903
- });
1346
+ // Functions for escaping and unescaping strings to/from HTML interpolation.
1347
+ var createEscaper = function(map) {
1348
+ var escaper = function(match) {
1349
+ return map[match];
1350
+ };
1351
+ // Regexes for identifying a key that needs to be escaped
1352
+ var source = '(?:' + _.keys(map).join('|') + ')';
1353
+ var testRegexp = RegExp(source);
1354
+ var replaceRegexp = RegExp(source, 'g');
1355
+ return function(string) {
1356
+ string = string == null ? '' : '' + string;
1357
+ return testRegexp.test(string) ? string.replace(replaceRegexp, escaper) : string;
1358
+ };
1359
+ };
1360
+ _.escape = createEscaper(escapeMap);
1361
+ _.unescape = createEscaper(unescapeMap);
1362
+
1363
+ // If the value of the named `property` is a function then invoke it with the
1364
+ // `object` as context; otherwise, return it.
1365
+ _.result = function(object, property, fallback) {
1366
+ var value = object == null ? void 0 : object[property];
1367
+ if (value === void 0) {
1368
+ value = fallback;
1369
+ }
1370
+ return _.isFunction(value) ? value.call(object) : value;
904
1371
  };
905
1372
 
906
1373
  // Generate a unique integer id (unique within the entire client session).
907
1374
  // Useful for temporary DOM ids.
908
1375
  var idCounter = 0;
909
1376
  _.uniqueId = function(prefix) {
910
- var id = idCounter++;
1377
+ var id = ++idCounter + '';
911
1378
  return prefix ? prefix + id : id;
912
1379
  };
913
1380
 
@@ -922,138 +1389,160 @@
922
1389
  // When customizing `templateSettings`, if you don't want to define an
923
1390
  // interpolation, evaluation or escaping regex, we need one that is
924
1391
  // guaranteed not to match.
925
- var noMatch = /.^/;
1392
+ var noMatch = /(.)^/;
926
1393
 
927
1394
  // Certain characters need to be escaped so that they can be put into a
928
1395
  // string literal.
929
1396
  var escapes = {
930
- '\\': '\\',
931
- "'": "'",
932
- 'r': '\r',
933
- 'n': '\n',
934
- 't': '\t',
935
- 'u2028': '\u2028',
936
- 'u2029': '\u2029'
937
- };
938
-
939
- for (var p in escapes) escapes[escapes[p]] = p;
940
- 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
- });
1397
+ "'": "'",
1398
+ '\\': '\\',
1399
+ '\r': 'r',
1400
+ '\n': 'n',
1401
+ '\u2028': 'u2028',
1402
+ '\u2029': 'u2029'
1403
+ };
1404
+
1405
+ var escaper = /\\|'|\r|\n|\u2028|\u2029/g;
1406
+
1407
+ var escapeChar = function(match) {
1408
+ return '\\' + escapes[match];
949
1409
  };
950
1410
 
951
1411
  // JavaScript micro-templating, similar to John Resig's implementation.
952
1412
  // Underscore templating handles arbitrary delimiters, preserves whitespace,
953
1413
  // and correctly escapes quotes within interpolated code.
954
- _.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";
1414
+ // NB: `oldSettings` only exists for backwards compatibility.
1415
+ _.template = function(text, settings, oldSettings) {
1416
+ if (!settings && oldSettings) settings = oldSettings;
1417
+ settings = _.defaults({}, settings, _.templateSettings);
1418
+
1419
+ // Combine delimiters into one regular expression via alternation.
1420
+ var matcher = RegExp([
1421
+ (settings.escape || noMatch).source,
1422
+ (settings.interpolate || noMatch).source,
1423
+ (settings.evaluate || noMatch).source
1424
+ ].join('|') + '|$', 'g');
1425
+
1426
+ // Compile the template source, escaping string literals appropriately.
1427
+ var index = 0;
1428
+ var source = "__p+='";
1429
+ text.replace(matcher, function(match, escape, interpolate, evaluate, offset) {
1430
+ source += text.slice(index, offset).replace(escaper, escapeChar);
1431
+ index = offset + match.length;
1432
+
1433
+ if (escape) {
1434
+ source += "'+\n((__t=(" + escape + "))==null?'':_.escape(__t))+\n'";
1435
+ } else if (interpolate) {
1436
+ source += "'+\n((__t=(" + interpolate + "))==null?'':__t)+\n'";
1437
+ } else if (evaluate) {
1438
+ source += "';\n" + evaluate + "\n__p+='";
1439
+ }
1440
+
1441
+ // Adobe VMs need the match returned to produce the correct offest.
1442
+ return match;
1443
+ });
1444
+ source += "';\n";
973
1445
 
974
1446
  // If a variable is not specified, place data values in local scope.
975
1447
  if (!settings.variable) source = 'with(obj||{}){\n' + source + '}\n';
976
1448
 
977
- source = "var __p='';" +
978
- "var print=function(){__p+=Array.prototype.join.call(arguments, '')};\n" +
979
- source + "return __p;\n";
1449
+ source = "var __t,__p='',__j=Array.prototype.join," +
1450
+ "print=function(){__p+=__j.call(arguments,'');};\n" +
1451
+ source + 'return __p;\n';
1452
+
1453
+ try {
1454
+ var render = new Function(settings.variable || 'obj', '_', source);
1455
+ } catch (e) {
1456
+ e.source = source;
1457
+ throw e;
1458
+ }
980
1459
 
981
- var render = new Function(settings.variable || 'obj', '_', source);
982
- if (data) return render(data, _);
983
1460
  var template = function(data) {
984
1461
  return render.call(this, data, _);
985
1462
  };
986
1463
 
987
- // Provide the compiled function source as a convenience for build time
988
- // precompilation.
989
- template.source = 'function(' + (settings.variable || 'obj') + '){\n' +
990
- source + '}';
1464
+ // Provide the compiled source as a convenience for precompilation.
1465
+ var argument = settings.variable || 'obj';
1466
+ template.source = 'function(' + argument + '){\n' + source + '}';
991
1467
 
992
1468
  return template;
993
1469
  };
994
1470
 
995
- // Add a "chain" function, which will delegate to the wrapper.
1471
+ // Add a "chain" function. Start chaining a wrapped Underscore object.
996
1472
  _.chain = function(obj) {
997
- return _(obj).chain();
1473
+ var instance = _(obj);
1474
+ instance._chain = true;
1475
+ return instance;
998
1476
  };
999
1477
 
1000
- // The OOP Wrapper
1478
+ // OOP
1001
1479
  // ---------------
1002
-
1003
1480
  // If Underscore is called as a function, it returns a wrapped object that
1004
1481
  // can be used OO-style. This wrapper holds altered versions of all the
1005
1482
  // 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
1483
 
1011
1484
  // Helper function to continue chaining intermediate results.
1012
- var result = function(obj, chain) {
1013
- return chain ? _(obj).chain() : obj;
1485
+ var result = function(instance, obj) {
1486
+ return instance._chain ? _(obj).chain() : obj;
1014
1487
  };
1015
1488
 
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
- };
1489
+ // Add your own custom functions to the Underscore object.
1490
+ _.mixin = function(obj) {
1491
+ _.each(_.functions(obj), function(name) {
1492
+ var func = _[name] = obj[name];
1493
+ _.prototype[name] = function() {
1494
+ var args = [this._wrapped];
1495
+ push.apply(args, arguments);
1496
+ return result(this, func.apply(_, args));
1497
+ };
1498
+ });
1023
1499
  };
1024
1500
 
1025
1501
  // Add all of the Underscore functions to the wrapper object.
1026
1502
  _.mixin(_);
1027
1503
 
1028
1504
  // Add all mutator Array functions to the wrapper.
1029
- each(['pop', 'push', 'reverse', 'shift', 'sort', 'splice', 'unshift'], function(name) {
1505
+ _.each(['pop', 'push', 'reverse', 'shift', 'sort', 'splice', 'unshift'], function(name) {
1030
1506
  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);
1507
+ _.prototype[name] = function() {
1508
+ var obj = this._wrapped;
1509
+ method.apply(obj, arguments);
1510
+ if ((name === 'shift' || name === 'splice') && obj.length === 0) delete obj[0];
1511
+ return result(this, obj);
1037
1512
  };
1038
1513
  });
1039
1514
 
1040
1515
  // Add all accessor Array functions to the wrapper.
1041
- each(['concat', 'join', 'slice'], function(name) {
1516
+ _.each(['concat', 'join', 'slice'], function(name) {
1042
1517
  var method = ArrayProto[name];
1043
- wrapper.prototype[name] = function() {
1044
- return result(method.apply(this._wrapped, arguments), this._chain);
1518
+ _.prototype[name] = function() {
1519
+ return result(this, method.apply(this._wrapped, arguments));
1045
1520
  };
1046
1521
  });
1047
1522
 
1048
- // Start chaining a wrapped Underscore object.
1049
- wrapper.prototype.chain = function() {
1050
- this._chain = true;
1051
- return this;
1052
- };
1053
-
1054
1523
  // Extracts the result from a wrapped and chained object.
1055
- wrapper.prototype.value = function() {
1524
+ _.prototype.value = function() {
1056
1525
  return this._wrapped;
1057
1526
  };
1058
1527
 
1059
- }).call(this);
1528
+ // Provide unwrapping proxy for some methods used in engine operations
1529
+ // such as arithmetic and JSON stringification.
1530
+ _.prototype.valueOf = _.prototype.toJSON = _.prototype.value;
1531
+
1532
+ _.prototype.toString = function() {
1533
+ return '' + this._wrapped;
1534
+ };
1535
+
1536
+ // AMD registration happens at the end for compatibility with AMD loaders
1537
+ // that may not enforce next-turn semantics on modules. Even though general
1538
+ // practice for AMD registration is to be anonymous, underscore registers
1539
+ // as a named module because, like jQuery, it is a base library that is
1540
+ // popular enough to be bundled in a third party lib, but not be part of
1541
+ // an AMD load request. Those cases could generate an error when an
1542
+ // anonymous define() is called outside of a loader request.
1543
+ if (typeof define === 'function' && define.amd) {
1544
+ define('underscore', [], function() {
1545
+ return _;
1546
+ });
1547
+ }
1548
+ }.call(this));