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.
- data/CHANGELOG +5 -0
- data/README.md +24 -18
- data/lib/assets/javascripts/backbone-support/support.js +1 -1
- data/lib/backbone-support/version.rb +1 -1
- data/spec/javascripts/composite_view_spec.js +2 -2
- data/spec/javascripts/observer_spec.js +1 -1
- data/spec/javascripts/support/jquery.js +9590 -11
- data/vendor/assets/javascripts/backbone.js +1145 -732
- data/vendor/assets/javascripts/underscore.js +668 -280
- metadata +1 -1
@@ -1,15 +1,13 @@
|
|
1
|
-
//
|
2
|
-
//
|
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
|
-
|
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
|
-
|
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
|
28
|
-
|
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) {
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
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
|
-
|
59
|
-
root['_'] = _;
|
64
|
+
root._ = _;
|
60
65
|
}
|
61
66
|
|
62
67
|
// Current version.
|
63
|
-
_.VERSION = '1.
|
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 (
|
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 (
|
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 =
|
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(
|
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
|
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
|
-
|
130
|
-
|
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
|
-
|
161
|
-
|
162
|
-
|
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
|
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
|
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
|
196
|
-
// Aliased as `
|
197
|
-
_.
|
198
|
-
|
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
|
-
|
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 (
|
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)
|
223
|
-
|
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)
|
234
|
-
|
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,
|
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
|
251
|
-
|
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
|
-
//
|
256
|
-
|
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
|
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
|
-
//
|
266
|
-
//
|
267
|
-
_.
|
268
|
-
|
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)
|
272
|
-
iterator(array[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(
|
279
|
-
if (!
|
280
|
-
if (
|
281
|
-
if (
|
282
|
-
|
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
|
-
|
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
|
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
|
302
|
-
//
|
303
|
-
// the
|
304
|
-
//
|
305
|
-
_.
|
306
|
-
return slice.call(array, (
|
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
|
-
//
|
310
|
-
|
311
|
-
|
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,
|
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
|
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
|
-
|
338
|
-
|
339
|
-
|
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(
|
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.
|
351
|
-
_.intersection =
|
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
|
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
|
363
|
-
|
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++)
|
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
|
-
|
387
|
-
|
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 (
|
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
|
-
|
399
|
-
|
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).
|
431
|
-
//
|
432
|
-
|
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(
|
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
|
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
|
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(
|
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
|
-
|
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
|
-
|
497
|
-
|
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
|
-
|
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]
|
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 =
|
704
|
+
var funcs = arguments;
|
525
705
|
return function() {
|
526
|
-
var args =
|
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) {
|
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 (
|
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
|
-
|
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
|
-
|
573
|
-
|
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
|
-
|
583
|
-
|
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
|
-
|
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 (
|
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
|
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)
|
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
|
-
//
|
666
|
-
|
667
|
-
|
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
|
-
//
|
671
|
-
|
672
|
-
|
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
|
-
//
|
676
|
-
|
677
|
-
|
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
|
681
|
-
_.
|
682
|
-
return
|
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`?
|
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
|
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
|
733
|
-
|
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
|
+
'&': '&',
|
1034
|
+
'<': '<',
|
1035
|
+
'>': '>',
|
1036
|
+
'"': '"',
|
1037
|
+
"'": ''',
|
1038
|
+
'/': '/'
|
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
|
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
|
-
|
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(
|
763
|
-
var
|
764
|
-
|
765
|
-
|
766
|
-
|
767
|
-
|
768
|
-
|
769
|
-
|
770
|
-
|
771
|
-
|
772
|
-
|
773
|
-
|
774
|
-
|
775
|
-
|
776
|
-
|
777
|
-
|
778
|
-
|
779
|
-
|
780
|
-
|
781
|
-
|
782
|
-
|
783
|
-
|
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
|
796
|
-
return
|
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
|
-
|
815
|
-
|
816
|
-
|
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
|
-
|
824
|
-
return result(method.apply(this._wrapped, arguments)
|
1207
|
+
_.prototype[name] = function() {
|
1208
|
+
return result.call(this, method.apply(this._wrapped, arguments));
|
825
1209
|
};
|
826
1210
|
});
|
827
1211
|
|
828
|
-
|
829
|
-
wrapper.prototype.chain = function() {
|
830
|
-
this._chain = true;
|
831
|
-
return this;
|
832
|
-
};
|
1212
|
+
_.extend(_.prototype, {
|
833
1213
|
|
834
|
-
|
835
|
-
|
836
|
-
|
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);
|