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