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