judge 1.1.0 → 1.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/.travis.yml +5 -1
- data/README.md +11 -4
- data/judge.gemspec +8 -9
- data/lib/generators/judge/templates/judge.js +142 -196
- data/lib/generators/judge/templates/underscore.js +138 -101
- data/lib/judge/version.rb +1 -1
- data/spec/javascripts/JudgeSpec.js +6 -5
- data/spec/javascripts/helpers/json2.js +46 -41
- data/spec/javascripts/helpers/underscore.js +305 -130
- data/spec/javascripts/support/jasmine_runner.rb +4 -15
- metadata +22 -23
@@ -1,5 +1,5 @@
|
|
1
|
-
// Underscore.js 1.1
|
2
|
-
// (c)
|
1
|
+
// Underscore.js 1.3.1
|
2
|
+
// (c) 2009-2012 Jeremy Ashkenas, DocumentCloud Inc.
|
3
3
|
// Underscore is freely distributable under the MIT license.
|
4
4
|
// Portions of Underscore are inspired or borrowed from Prototype,
|
5
5
|
// Oliver Steele's Functional, and John Resig's Micro-Templating.
|
@@ -48,37 +48,39 @@
|
|
48
48
|
// Create a safe reference to the Underscore object for use below.
|
49
49
|
var _ = function(obj) { return new wrapper(obj); };
|
50
50
|
|
51
|
-
// Export the Underscore object for **
|
52
|
-
// for the old `require()` API. If we're
|
53
|
-
// global object
|
54
|
-
|
55
|
-
|
56
|
-
|
51
|
+
// Export the Underscore object for **Node.js**, with
|
52
|
+
// backwards-compatibility for the old `require()` API. If we're in
|
53
|
+
// the browser, add `_` as a global object via a string identifier,
|
54
|
+
// for Closure Compiler "advanced" mode.
|
55
|
+
if (typeof exports !== 'undefined') {
|
56
|
+
if (typeof module !== 'undefined' && module.exports) {
|
57
|
+
exports = module.exports = _;
|
58
|
+
}
|
59
|
+
exports._ = _;
|
57
60
|
} else {
|
58
|
-
// Exported as a string, for Closure Compiler "advanced" mode.
|
59
61
|
root['_'] = _;
|
60
62
|
}
|
61
63
|
|
62
64
|
// Current version.
|
63
|
-
_.VERSION = '1.1
|
65
|
+
_.VERSION = '1.3.1';
|
64
66
|
|
65
67
|
// Collection Functions
|
66
68
|
// --------------------
|
67
69
|
|
68
70
|
// The cornerstone, an `each` implementation, aka `forEach`.
|
69
|
-
// Handles objects
|
71
|
+
// Handles objects with the built-in `forEach`, arrays, and raw objects.
|
70
72
|
// Delegates to **ECMAScript 5**'s native `forEach` if available.
|
71
73
|
var each = _.each = _.forEach = function(obj, iterator, context) {
|
72
74
|
if (obj == null) return;
|
73
75
|
if (nativeForEach && obj.forEach === nativeForEach) {
|
74
76
|
obj.forEach(iterator, context);
|
75
|
-
} else if (
|
77
|
+
} else if (obj.length === +obj.length) {
|
76
78
|
for (var i = 0, l = obj.length; i < l; i++) {
|
77
79
|
if (i in obj && iterator.call(context, obj[i], i, obj) === breaker) return;
|
78
80
|
}
|
79
81
|
} else {
|
80
82
|
for (var key in obj) {
|
81
|
-
if (
|
83
|
+
if (_.has(obj, key)) {
|
82
84
|
if (iterator.call(context, obj[key], key, obj) === breaker) return;
|
83
85
|
}
|
84
86
|
}
|
@@ -87,47 +89,50 @@
|
|
87
89
|
|
88
90
|
// Return the results of applying the iterator to each element.
|
89
91
|
// Delegates to **ECMAScript 5**'s native `map` if available.
|
90
|
-
_.map = function(obj, iterator, context) {
|
92
|
+
_.map = _.collect = function(obj, iterator, context) {
|
91
93
|
var results = [];
|
92
94
|
if (obj == null) return results;
|
93
95
|
if (nativeMap && obj.map === nativeMap) return obj.map(iterator, context);
|
94
96
|
each(obj, function(value, index, list) {
|
95
97
|
results[results.length] = iterator.call(context, value, index, list);
|
96
98
|
});
|
99
|
+
if (obj.length === +obj.length) results.length = obj.length;
|
97
100
|
return results;
|
98
101
|
};
|
99
102
|
|
100
103
|
// **Reduce** builds up a single result from a list of values, aka `inject`,
|
101
104
|
// or `foldl`. Delegates to **ECMAScript 5**'s native `reduce` if available.
|
102
105
|
_.reduce = _.foldl = _.inject = function(obj, iterator, memo, context) {
|
103
|
-
var initial =
|
106
|
+
var initial = arguments.length > 2;
|
104
107
|
if (obj == null) obj = [];
|
105
108
|
if (nativeReduce && obj.reduce === nativeReduce) {
|
106
109
|
if (context) iterator = _.bind(iterator, context);
|
107
110
|
return initial ? obj.reduce(iterator, memo) : obj.reduce(iterator);
|
108
111
|
}
|
109
112
|
each(obj, function(value, index, list) {
|
110
|
-
if (!initial
|
113
|
+
if (!initial) {
|
111
114
|
memo = value;
|
112
115
|
initial = true;
|
113
116
|
} else {
|
114
117
|
memo = iterator.call(context, memo, value, index, list);
|
115
118
|
}
|
116
119
|
});
|
117
|
-
if (!initial) throw new TypeError(
|
120
|
+
if (!initial) throw new TypeError('Reduce of empty array with no initial value');
|
118
121
|
return memo;
|
119
122
|
};
|
120
123
|
|
121
124
|
// The right-associative version of reduce, also known as `foldr`.
|
122
125
|
// Delegates to **ECMAScript 5**'s native `reduceRight` if available.
|
123
126
|
_.reduceRight = _.foldr = function(obj, iterator, memo, context) {
|
127
|
+
var initial = arguments.length > 2;
|
124
128
|
if (obj == null) obj = [];
|
125
129
|
if (nativeReduceRight && obj.reduceRight === nativeReduceRight) {
|
126
130
|
if (context) iterator = _.bind(iterator, context);
|
127
|
-
return
|
131
|
+
return initial ? obj.reduceRight(iterator, memo) : obj.reduceRight(iterator);
|
128
132
|
}
|
129
|
-
var reversed =
|
130
|
-
|
133
|
+
var reversed = _.toArray(obj).reverse();
|
134
|
+
if (context && !initial) iterator = _.bind(iterator, context);
|
135
|
+
return initial ? _.reduce(reversed, iterator, memo, context) : _.reduce(reversed, iterator);
|
131
136
|
};
|
132
137
|
|
133
138
|
// Return the first value which passes a truth test. Aliased as `detect`.
|
@@ -187,9 +192,9 @@
|
|
187
192
|
if (obj == null) return result;
|
188
193
|
if (nativeSome && obj.some === nativeSome) return obj.some(iterator, context);
|
189
194
|
each(obj, function(value, index, list) {
|
190
|
-
if (result = iterator.call(context, value, index, list)) return breaker;
|
195
|
+
if (result || (result = iterator.call(context, value, index, list))) return breaker;
|
191
196
|
});
|
192
|
-
return result;
|
197
|
+
return !!result;
|
193
198
|
};
|
194
199
|
|
195
200
|
// Determine if a given value is included in the array or object using `===`.
|
@@ -198,8 +203,8 @@
|
|
198
203
|
var found = false;
|
199
204
|
if (obj == null) return found;
|
200
205
|
if (nativeIndexOf && obj.indexOf === nativeIndexOf) return obj.indexOf(target) != -1;
|
201
|
-
any(obj, function(value) {
|
202
|
-
|
206
|
+
found = any(obj, function(value) {
|
207
|
+
return value === target;
|
203
208
|
});
|
204
209
|
return found;
|
205
210
|
};
|
@@ -208,7 +213,7 @@
|
|
208
213
|
_.invoke = function(obj, method) {
|
209
214
|
var args = slice.call(arguments, 2);
|
210
215
|
return _.map(obj, function(value) {
|
211
|
-
return (method
|
216
|
+
return (_.isFunction(method) ? method || value : value[method]).apply(value, args);
|
212
217
|
});
|
213
218
|
};
|
214
219
|
|
@@ -220,6 +225,7 @@
|
|
220
225
|
// Return the maximum element or (element-based computation).
|
221
226
|
_.max = function(obj, iterator, context) {
|
222
227
|
if (!iterator && _.isArray(obj)) return Math.max.apply(Math, obj);
|
228
|
+
if (!iterator && _.isEmpty(obj)) return -Infinity;
|
223
229
|
var result = {computed : -Infinity};
|
224
230
|
each(obj, function(value, index, list) {
|
225
231
|
var computed = iterator ? iterator.call(context, value, index, list) : value;
|
@@ -231,6 +237,7 @@
|
|
231
237
|
// Return the minimum element (or element-based computation).
|
232
238
|
_.min = function(obj, iterator, context) {
|
233
239
|
if (!iterator && _.isArray(obj)) return Math.min.apply(Math, obj);
|
240
|
+
if (!iterator && _.isEmpty(obj)) return Infinity;
|
234
241
|
var result = {computed : Infinity};
|
235
242
|
each(obj, function(value, index, list) {
|
236
243
|
var computed = iterator ? iterator.call(context, value, index, list) : value;
|
@@ -239,6 +246,21 @@
|
|
239
246
|
return result.value;
|
240
247
|
};
|
241
248
|
|
249
|
+
// Shuffle an array.
|
250
|
+
_.shuffle = function(obj) {
|
251
|
+
var shuffled = [], rand;
|
252
|
+
each(obj, function(value, index, list) {
|
253
|
+
if (index == 0) {
|
254
|
+
shuffled[0] = value;
|
255
|
+
} else {
|
256
|
+
rand = Math.floor(Math.random() * (index + 1));
|
257
|
+
shuffled[index] = shuffled[rand];
|
258
|
+
shuffled[rand] = value;
|
259
|
+
}
|
260
|
+
});
|
261
|
+
return shuffled;
|
262
|
+
};
|
263
|
+
|
242
264
|
// Sort the object's values by a criterion produced by an iterator.
|
243
265
|
_.sortBy = function(obj, iterator, context) {
|
244
266
|
return _.pluck(_.map(obj, function(value, index, list) {
|
@@ -252,9 +274,11 @@
|
|
252
274
|
}), 'value');
|
253
275
|
};
|
254
276
|
|
255
|
-
// Groups the object's values by a criterion
|
256
|
-
|
277
|
+
// Groups the object's values by a criterion. Pass either a string attribute
|
278
|
+
// to group by, or a function that returns the criterion.
|
279
|
+
_.groupBy = function(obj, val) {
|
257
280
|
var result = {};
|
281
|
+
var iterator = _.isFunction(val) ? val : function(obj) { return obj[val]; };
|
258
282
|
each(obj, function(value, index) {
|
259
283
|
var key = iterator(value, index);
|
260
284
|
(result[key] || (result[key] = [])).push(value);
|
@@ -278,7 +302,7 @@
|
|
278
302
|
_.toArray = function(iterable) {
|
279
303
|
if (!iterable) return [];
|
280
304
|
if (iterable.toArray) return iterable.toArray();
|
281
|
-
if (_.isArray(iterable)) return iterable;
|
305
|
+
if (_.isArray(iterable)) return slice.call(iterable);
|
282
306
|
if (_.isArguments(iterable)) return slice.call(iterable);
|
283
307
|
return _.values(iterable);
|
284
308
|
};
|
@@ -298,6 +322,24 @@
|
|
298
322
|
return (n != null) && !guard ? slice.call(array, 0, n) : array[0];
|
299
323
|
};
|
300
324
|
|
325
|
+
// Returns everything but the last entry of the array. Especcialy useful on
|
326
|
+
// the arguments object. Passing **n** will return all the values in
|
327
|
+
// the array, excluding the last N. The **guard** check allows it to work with
|
328
|
+
// `_.map`.
|
329
|
+
_.initial = function(array, n, guard) {
|
330
|
+
return slice.call(array, 0, array.length - ((n == null) || guard ? 1 : n));
|
331
|
+
};
|
332
|
+
|
333
|
+
// Get the last element of an array. Passing **n** will return the last N
|
334
|
+
// values in the array. The **guard** check allows it to work with `_.map`.
|
335
|
+
_.last = function(array, n, guard) {
|
336
|
+
if ((n != null) && !guard) {
|
337
|
+
return slice.call(array, Math.max(array.length - n, 0));
|
338
|
+
} else {
|
339
|
+
return array[array.length - 1];
|
340
|
+
}
|
341
|
+
};
|
342
|
+
|
301
343
|
// Returns everything but the first entry of the array. Aliased as `tail`.
|
302
344
|
// Especially useful on the arguments object. Passing an **index** will return
|
303
345
|
// the rest of the values in the array from that index onward. The **guard**
|
@@ -306,20 +348,15 @@
|
|
306
348
|
return slice.call(array, (index == null) || guard ? 1 : index);
|
307
349
|
};
|
308
350
|
|
309
|
-
// Get the last element of an array.
|
310
|
-
_.last = function(array) {
|
311
|
-
return array[array.length - 1];
|
312
|
-
};
|
313
|
-
|
314
351
|
// Trim out all falsy values from an array.
|
315
352
|
_.compact = function(array) {
|
316
353
|
return _.filter(array, function(value){ return !!value; });
|
317
354
|
};
|
318
355
|
|
319
356
|
// Return a completely flattened version of an array.
|
320
|
-
_.flatten = function(array) {
|
357
|
+
_.flatten = function(array, shallow) {
|
321
358
|
return _.reduce(array, function(memo, value) {
|
322
|
-
if (_.isArray(value)) return memo.concat(_.flatten(value));
|
359
|
+
if (_.isArray(value)) return memo.concat(shallow ? value : _.flatten(value));
|
323
360
|
memo[memo.length] = value;
|
324
361
|
return memo;
|
325
362
|
}, []);
|
@@ -327,23 +364,34 @@
|
|
327
364
|
|
328
365
|
// Return a version of the array that does not contain the specified value(s).
|
329
366
|
_.without = function(array) {
|
330
|
-
|
331
|
-
return _.filter(array, function(value){ return !_.include(values, value); });
|
367
|
+
return _.difference(array, slice.call(arguments, 1));
|
332
368
|
};
|
333
369
|
|
334
370
|
// Produce a duplicate-free version of the array. If the array has already
|
335
371
|
// been sorted, you have the option of using a faster algorithm.
|
336
372
|
// Aliased as `unique`.
|
337
|
-
_.uniq = _.unique = function(array, isSorted) {
|
338
|
-
|
339
|
-
|
373
|
+
_.uniq = _.unique = function(array, isSorted, iterator) {
|
374
|
+
var initial = iterator ? _.map(array, iterator) : array;
|
375
|
+
var result = [];
|
376
|
+
_.reduce(initial, function(memo, el, i) {
|
377
|
+
if (0 == i || (isSorted === true ? _.last(memo) != el : !_.include(memo, el))) {
|
378
|
+
memo[memo.length] = el;
|
379
|
+
result[result.length] = array[i];
|
380
|
+
}
|
340
381
|
return memo;
|
341
382
|
}, []);
|
383
|
+
return result;
|
384
|
+
};
|
385
|
+
|
386
|
+
// Produce an array that contains the union: each distinct element from all of
|
387
|
+
// the passed-in arrays.
|
388
|
+
_.union = function() {
|
389
|
+
return _.uniq(_.flatten(arguments, true));
|
342
390
|
};
|
343
391
|
|
344
392
|
// Produce an array that contains every item shared between all the
|
345
|
-
// passed-in arrays.
|
346
|
-
_.intersect = function(array) {
|
393
|
+
// passed-in arrays. (Aliased as "intersect" for back-compat.)
|
394
|
+
_.intersection = _.intersect = function(array) {
|
347
395
|
var rest = slice.call(arguments, 1);
|
348
396
|
return _.filter(_.uniq(array), function(item) {
|
349
397
|
return _.every(rest, function(other) {
|
@@ -352,6 +400,13 @@
|
|
352
400
|
});
|
353
401
|
};
|
354
402
|
|
403
|
+
// Take the difference between one array and a number of other arrays.
|
404
|
+
// Only the elements present in just the first array will remain.
|
405
|
+
_.difference = function(array) {
|
406
|
+
var rest = _.flatten(slice.call(arguments, 1));
|
407
|
+
return _.filter(array, function(value){ return !_.include(rest, value); });
|
408
|
+
};
|
409
|
+
|
355
410
|
// Zip together multiple lists into a single array -- elements that share
|
356
411
|
// an index go together.
|
357
412
|
_.zip = function() {
|
@@ -376,17 +431,16 @@
|
|
376
431
|
return array[i] === item ? i : -1;
|
377
432
|
}
|
378
433
|
if (nativeIndexOf && array.indexOf === nativeIndexOf) return array.indexOf(item);
|
379
|
-
for (i = 0, l = array.length; i < l; i++) if (array[i] === item) return i;
|
434
|
+
for (i = 0, l = array.length; i < l; i++) if (i in array && array[i] === item) return i;
|
380
435
|
return -1;
|
381
436
|
};
|
382
437
|
|
383
|
-
|
384
438
|
// Delegates to **ECMAScript 5**'s native `lastIndexOf` if available.
|
385
439
|
_.lastIndexOf = function(array, item) {
|
386
440
|
if (array == null) return -1;
|
387
441
|
if (nativeLastIndexOf && array.lastIndexOf === nativeLastIndexOf) return array.lastIndexOf(item);
|
388
442
|
var i = array.length;
|
389
|
-
while (i--) if (array[i] === item) return i;
|
443
|
+
while (i--) if (i in array && array[i] === item) return i;
|
390
444
|
return -1;
|
391
445
|
};
|
392
446
|
|
@@ -415,15 +469,25 @@
|
|
415
469
|
// Function (ahem) Functions
|
416
470
|
// ------------------
|
417
471
|
|
472
|
+
// Reusable constructor function for prototype setting.
|
473
|
+
var ctor = function(){};
|
474
|
+
|
418
475
|
// Create a function bound to a given object (assigning `this`, and arguments,
|
419
476
|
// optionally). Binding with arguments is also known as `curry`.
|
420
477
|
// Delegates to **ECMAScript 5**'s native `Function.bind` if available.
|
421
478
|
// We check for `func.bind` first, to fail fast when `func` is undefined.
|
422
|
-
_.bind = function(func,
|
479
|
+
_.bind = function bind(func, context) {
|
480
|
+
var bound, args;
|
423
481
|
if (func.bind === nativeBind && nativeBind) return nativeBind.apply(func, slice.call(arguments, 1));
|
424
|
-
|
425
|
-
|
426
|
-
|
482
|
+
if (!_.isFunction(func)) throw new TypeError;
|
483
|
+
args = slice.call(arguments, 2);
|
484
|
+
return bound = function() {
|
485
|
+
if (!(this instanceof bound)) return func.apply(context, args.concat(slice.call(arguments)));
|
486
|
+
ctor.prototype = func.prototype;
|
487
|
+
var self = new ctor;
|
488
|
+
var result = func.apply(self, args.concat(slice.call(arguments)));
|
489
|
+
if (Object(result) === result) return result;
|
490
|
+
return self;
|
427
491
|
};
|
428
492
|
};
|
429
493
|
|
@@ -442,7 +506,7 @@
|
|
442
506
|
hasher || (hasher = _.identity);
|
443
507
|
return function() {
|
444
508
|
var key = hasher.apply(this, arguments);
|
445
|
-
return
|
509
|
+
return _.has(memo, key) ? memo[key] : (memo[key] = func.apply(this, arguments));
|
446
510
|
};
|
447
511
|
};
|
448
512
|
|
@@ -459,31 +523,43 @@
|
|
459
523
|
return _.delay.apply(_, [func, 1].concat(slice.call(arguments, 1)));
|
460
524
|
};
|
461
525
|
|
462
|
-
//
|
463
|
-
|
464
|
-
|
526
|
+
// Returns a function, that, when invoked, will only be triggered at most once
|
527
|
+
// during a given window of time.
|
528
|
+
_.throttle = function(func, wait) {
|
529
|
+
var context, args, timeout, throttling, more;
|
530
|
+
var whenDone = _.debounce(function(){ more = throttling = false; }, wait);
|
465
531
|
return function() {
|
466
|
-
|
467
|
-
var
|
532
|
+
context = this; args = arguments;
|
533
|
+
var later = function() {
|
468
534
|
timeout = null;
|
469
|
-
func.apply(context, args);
|
535
|
+
if (more) func.apply(context, args);
|
536
|
+
whenDone();
|
470
537
|
};
|
471
|
-
if (
|
472
|
-
if (
|
538
|
+
if (!timeout) timeout = setTimeout(later, wait);
|
539
|
+
if (throttling) {
|
540
|
+
more = true;
|
541
|
+
} else {
|
542
|
+
func.apply(context, args);
|
543
|
+
}
|
544
|
+
whenDone();
|
545
|
+
throttling = true;
|
473
546
|
};
|
474
547
|
};
|
475
548
|
|
476
|
-
// Returns a function, that, when invoked, will only be triggered at most once
|
477
|
-
// during a given window of time.
|
478
|
-
_.throttle = function(func, wait) {
|
479
|
-
return limit(func, wait, false);
|
480
|
-
};
|
481
|
-
|
482
549
|
// Returns a function, that, as long as it continues to be invoked, will not
|
483
550
|
// be triggered. The function will be called after it stops being called for
|
484
551
|
// N milliseconds.
|
485
552
|
_.debounce = function(func, wait) {
|
486
|
-
|
553
|
+
var timeout;
|
554
|
+
return function() {
|
555
|
+
var context = this, args = arguments;
|
556
|
+
var later = function() {
|
557
|
+
timeout = null;
|
558
|
+
func.apply(context, args);
|
559
|
+
};
|
560
|
+
clearTimeout(timeout);
|
561
|
+
timeout = setTimeout(later, wait);
|
562
|
+
};
|
487
563
|
};
|
488
564
|
|
489
565
|
// Returns a function that will be executed at most one time, no matter how
|
@@ -502,7 +578,7 @@
|
|
502
578
|
// conditionally execute the original function.
|
503
579
|
_.wrap = function(func, wrapper) {
|
504
580
|
return function() {
|
505
|
-
var args = [func].concat(slice.call(arguments));
|
581
|
+
var args = [func].concat(slice.call(arguments, 0));
|
506
582
|
return wrapper.apply(this, args);
|
507
583
|
};
|
508
584
|
};
|
@@ -510,9 +586,9 @@
|
|
510
586
|
// Returns a function that is the composition of a list of functions, each
|
511
587
|
// consuming the return value of the function that follows.
|
512
588
|
_.compose = function() {
|
513
|
-
var funcs =
|
589
|
+
var funcs = arguments;
|
514
590
|
return function() {
|
515
|
-
var args =
|
591
|
+
var args = arguments;
|
516
592
|
for (var i = funcs.length - 1; i >= 0; i--) {
|
517
593
|
args = [funcs[i].apply(this, args)];
|
518
594
|
}
|
@@ -522,12 +598,12 @@
|
|
522
598
|
|
523
599
|
// Returns a function that will only be executed after being called N times.
|
524
600
|
_.after = function(times, func) {
|
601
|
+
if (times <= 0) return func();
|
525
602
|
return function() {
|
526
603
|
if (--times < 1) { return func.apply(this, arguments); }
|
527
604
|
};
|
528
605
|
};
|
529
606
|
|
530
|
-
|
531
607
|
// Object Functions
|
532
608
|
// ----------------
|
533
609
|
|
@@ -536,7 +612,7 @@
|
|
536
612
|
_.keys = nativeKeys || function(obj) {
|
537
613
|
if (obj !== Object(obj)) throw new TypeError('Invalid object');
|
538
614
|
var keys = [];
|
539
|
-
for (var key in obj) if (
|
615
|
+
for (var key in obj) if (_.has(obj, key)) keys[keys.length] = key;
|
540
616
|
return keys;
|
541
617
|
};
|
542
618
|
|
@@ -548,14 +624,18 @@
|
|
548
624
|
// Return a sorted list of the function names available on the object.
|
549
625
|
// Aliased as `methods`
|
550
626
|
_.functions = _.methods = function(obj) {
|
551
|
-
|
627
|
+
var names = [];
|
628
|
+
for (var key in obj) {
|
629
|
+
if (_.isFunction(obj[key])) names.push(key);
|
630
|
+
}
|
631
|
+
return names.sort();
|
552
632
|
};
|
553
633
|
|
554
634
|
// Extend a given object with all the properties in passed-in object(s).
|
555
635
|
_.extend = function(obj) {
|
556
636
|
each(slice.call(arguments, 1), function(source) {
|
557
637
|
for (var prop in source) {
|
558
|
-
|
638
|
+
obj[prop] = source[prop];
|
559
639
|
}
|
560
640
|
});
|
561
641
|
return obj;
|
@@ -573,6 +653,7 @@
|
|
573
653
|
|
574
654
|
// Create a (shallow-cloned) duplicate of an object.
|
575
655
|
_.clone = function(obj) {
|
656
|
+
if (!_.isObject(obj)) return obj;
|
576
657
|
return _.isArray(obj) ? obj.slice() : _.extend({}, obj);
|
577
658
|
};
|
578
659
|
|
@@ -584,50 +665,104 @@
|
|
584
665
|
return obj;
|
585
666
|
};
|
586
667
|
|
587
|
-
//
|
588
|
-
|
589
|
-
//
|
590
|
-
|
591
|
-
|
592
|
-
|
593
|
-
if (
|
594
|
-
// Basic equality test (watch out for coercions).
|
595
|
-
if (a == b) return true;
|
596
|
-
// One is falsy and the other truthy.
|
597
|
-
if ((!a && b) || (a && !b)) return false;
|
668
|
+
// Internal recursive comparison function.
|
669
|
+
function eq(a, b, stack) {
|
670
|
+
// Identical objects are equal. `0 === -0`, but they aren't identical.
|
671
|
+
// See the Harmony `egal` proposal: http://wiki.ecmascript.org/doku.php?id=harmony:egal.
|
672
|
+
if (a === b) return a !== 0 || 1 / a == 1 / b;
|
673
|
+
// A strict comparison is necessary because `null == undefined`.
|
674
|
+
if (a == null || b == null) return a === b;
|
598
675
|
// Unwrap any wrapped objects.
|
599
676
|
if (a._chain) a = a._wrapped;
|
600
677
|
if (b._chain) b = b._wrapped;
|
601
|
-
//
|
602
|
-
if (a.isEqual) return a.isEqual(b);
|
603
|
-
if (b.isEqual) return b.isEqual(a);
|
604
|
-
//
|
605
|
-
|
606
|
-
|
607
|
-
|
608
|
-
|
609
|
-
|
610
|
-
|
611
|
-
|
612
|
-
|
613
|
-
|
614
|
-
|
615
|
-
|
616
|
-
|
617
|
-
|
618
|
-
|
619
|
-
|
620
|
-
|
621
|
-
|
622
|
-
|
623
|
-
|
624
|
-
|
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);
|
681
|
+
// Compare `[[Class]]` names.
|
682
|
+
var className = toString.call(a);
|
683
|
+
if (className != toString.call(b)) return false;
|
684
|
+
switch (className) {
|
685
|
+
// Strings, numbers, dates, and booleans are compared by value.
|
686
|
+
case '[object String]':
|
687
|
+
// Primitives and their corresponding object wrappers are equivalent; thus, `"5"` is
|
688
|
+
// equivalent to `new String("5")`.
|
689
|
+
return a == String(b);
|
690
|
+
case '[object Number]':
|
691
|
+
// `NaN`s are equivalent, but non-reflexive. An `egal` comparison is performed for
|
692
|
+
// other numeric values.
|
693
|
+
return a != +a ? b != +b : (a == 0 ? 1 / a == 1 / b : a == +b);
|
694
|
+
case '[object Date]':
|
695
|
+
case '[object Boolean]':
|
696
|
+
// Coerce dates and booleans to numeric primitive values. Dates are compared by their
|
697
|
+
// millisecond representations. Note that invalid dates with millisecond representations
|
698
|
+
// of `NaN` are not equivalent.
|
699
|
+
return +a == +b;
|
700
|
+
// RegExps are compared by their source patterns and flags.
|
701
|
+
case '[object RegExp]':
|
702
|
+
return a.source == b.source &&
|
703
|
+
a.global == b.global &&
|
704
|
+
a.multiline == b.multiline &&
|
705
|
+
a.ignoreCase == b.ignoreCase;
|
706
|
+
}
|
707
|
+
if (typeof a != 'object' || typeof b != 'object') return false;
|
708
|
+
// Assume equality for cyclic structures. The algorithm for detecting cyclic
|
709
|
+
// structures is adapted from ES 5.1 section 15.12.3, abstract operation `JO`.
|
710
|
+
var length = stack.length;
|
711
|
+
while (length--) {
|
712
|
+
// Linear search. Performance is inversely proportional to the number of
|
713
|
+
// unique nested structures.
|
714
|
+
if (stack[length] == a) return true;
|
715
|
+
}
|
716
|
+
// Add the first object to the stack of traversed objects.
|
717
|
+
stack.push(a);
|
718
|
+
var size = 0, result = true;
|
719
|
+
// Recursively compare objects and arrays.
|
720
|
+
if (className == '[object Array]') {
|
721
|
+
// Compare array lengths to determine if a deep comparison is necessary.
|
722
|
+
size = a.length;
|
723
|
+
result = size == b.length;
|
724
|
+
if (result) {
|
725
|
+
// Deep compare the contents, ignoring non-numeric properties.
|
726
|
+
while (size--) {
|
727
|
+
// Ensure commutative equality for sparse arrays.
|
728
|
+
if (!(result = size in a == size in b && eq(a[size], b[size], stack))) break;
|
729
|
+
}
|
730
|
+
}
|
731
|
+
} else {
|
732
|
+
// Objects with different constructors are not equivalent.
|
733
|
+
if ('constructor' in a != 'constructor' in b || a.constructor != b.constructor) return false;
|
734
|
+
// Deep compare objects.
|
735
|
+
for (var key in a) {
|
736
|
+
if (_.has(a, key)) {
|
737
|
+
// Count the expected number of properties.
|
738
|
+
size++;
|
739
|
+
// Deep compare each member.
|
740
|
+
if (!(result = _.has(b, key) && eq(a[key], b[key], stack))) break;
|
741
|
+
}
|
742
|
+
}
|
743
|
+
// Ensure that both objects contain the same number of properties.
|
744
|
+
if (result) {
|
745
|
+
for (key in b) {
|
746
|
+
if (_.has(b, key) && !(size--)) break;
|
747
|
+
}
|
748
|
+
result = !size;
|
749
|
+
}
|
750
|
+
}
|
751
|
+
// Remove the first object from the stack of traversed objects.
|
752
|
+
stack.pop();
|
753
|
+
return result;
|
754
|
+
}
|
755
|
+
|
756
|
+
// Perform a deep comparison to check if two objects are equal.
|
757
|
+
_.isEqual = function(a, b) {
|
758
|
+
return eq(a, b, []);
|
625
759
|
};
|
626
760
|
|
627
|
-
// Is a given array or object empty?
|
761
|
+
// Is a given array, string, or object empty?
|
762
|
+
// An "empty" object has no enumerable own-properties.
|
628
763
|
_.isEmpty = function(obj) {
|
629
764
|
if (_.isArray(obj) || _.isString(obj)) return obj.length === 0;
|
630
|
-
for (var key in obj) if (
|
765
|
+
for (var key in obj) if (_.has(obj, key)) return false;
|
631
766
|
return true;
|
632
767
|
};
|
633
768
|
|
@@ -639,7 +774,7 @@
|
|
639
774
|
// Is a given value an array?
|
640
775
|
// Delegates to ECMA5's native Array.isArray
|
641
776
|
_.isArray = nativeIsArray || function(obj) {
|
642
|
-
return toString.call(obj)
|
777
|
+
return toString.call(obj) == '[object Array]';
|
643
778
|
};
|
644
779
|
|
645
780
|
// Is a given variable an object?
|
@@ -649,43 +784,48 @@
|
|
649
784
|
|
650
785
|
// Is a given variable an arguments object?
|
651
786
|
_.isArguments = function(obj) {
|
652
|
-
return
|
787
|
+
return toString.call(obj) == '[object Arguments]';
|
653
788
|
};
|
789
|
+
if (!_.isArguments(arguments)) {
|
790
|
+
_.isArguments = function(obj) {
|
791
|
+
return !!(obj && _.has(obj, 'callee'));
|
792
|
+
};
|
793
|
+
}
|
654
794
|
|
655
795
|
// Is a given value a function?
|
656
796
|
_.isFunction = function(obj) {
|
657
|
-
return
|
797
|
+
return toString.call(obj) == '[object Function]';
|
658
798
|
};
|
659
799
|
|
660
800
|
// Is a given value a string?
|
661
801
|
_.isString = function(obj) {
|
662
|
-
return
|
802
|
+
return toString.call(obj) == '[object String]';
|
663
803
|
};
|
664
804
|
|
665
805
|
// Is a given value a number?
|
666
806
|
_.isNumber = function(obj) {
|
667
|
-
return
|
807
|
+
return toString.call(obj) == '[object Number]';
|
668
808
|
};
|
669
809
|
|
670
|
-
// Is the given value `NaN`?
|
671
|
-
// that does not equal itself.
|
810
|
+
// Is the given value `NaN`?
|
672
811
|
_.isNaN = function(obj) {
|
812
|
+
// `NaN` is the only value for which `===` is not reflexive.
|
673
813
|
return obj !== obj;
|
674
814
|
};
|
675
815
|
|
676
816
|
// Is a given value a boolean?
|
677
817
|
_.isBoolean = function(obj) {
|
678
|
-
return obj === true || obj === false;
|
818
|
+
return obj === true || obj === false || toString.call(obj) == '[object Boolean]';
|
679
819
|
};
|
680
820
|
|
681
821
|
// Is a given value a date?
|
682
822
|
_.isDate = function(obj) {
|
683
|
-
return
|
823
|
+
return toString.call(obj) == '[object Date]';
|
684
824
|
};
|
685
825
|
|
686
826
|
// Is the given value a regular expression?
|
687
827
|
_.isRegExp = function(obj) {
|
688
|
-
return
|
828
|
+
return toString.call(obj) == '[object RegExp]';
|
689
829
|
};
|
690
830
|
|
691
831
|
// Is a given value equal to null?
|
@@ -698,6 +838,11 @@
|
|
698
838
|
return obj === void 0;
|
699
839
|
};
|
700
840
|
|
841
|
+
// Has own property?
|
842
|
+
_.has = function(obj, key) {
|
843
|
+
return hasOwnProperty.call(obj, key);
|
844
|
+
};
|
845
|
+
|
701
846
|
// Utility Functions
|
702
847
|
// -----------------
|
703
848
|
|
@@ -718,6 +863,11 @@
|
|
718
863
|
for (var i = 0; i < n; i++) iterator.call(context, i);
|
719
864
|
};
|
720
865
|
|
866
|
+
// Escape a string for HTML interpolation.
|
867
|
+
_.escape = function(string) {
|
868
|
+
return (''+string).replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>').replace(/"/g, '"').replace(/'/g, ''').replace(/\//g,'/');
|
869
|
+
};
|
870
|
+
|
721
871
|
// Add your own custom functions to the Underscore object, ensuring that
|
722
872
|
// they're correctly added to the OOP wrapper as well.
|
723
873
|
_.mixin = function(obj) {
|
@@ -738,7 +888,19 @@
|
|
738
888
|
// following template settings to use alternative delimiters.
|
739
889
|
_.templateSettings = {
|
740
890
|
evaluate : /<%([\s\S]+?)%>/g,
|
741
|
-
interpolate : /<%=([\s\S]+?)%>/g
|
891
|
+
interpolate : /<%=([\s\S]+?)%>/g,
|
892
|
+
escape : /<%-([\s\S]+?)%>/g
|
893
|
+
};
|
894
|
+
|
895
|
+
// When customizing `templateSettings`, if you don't want to define an
|
896
|
+
// interpolation, evaluation or escaping regex, we need one that is
|
897
|
+
// guaranteed not to match.
|
898
|
+
var noMatch = /.^/;
|
899
|
+
|
900
|
+
// Within an interpolation, evaluation, or escaping, remove HTML escaping
|
901
|
+
// that had been previously added.
|
902
|
+
var unescape = function(code) {
|
903
|
+
return code.replace(/\\\\/g, '\\').replace(/\\'/g, "'");
|
742
904
|
};
|
743
905
|
|
744
906
|
// JavaScript micro-templating, similar to John Resig's implementation.
|
@@ -750,19 +912,29 @@
|
|
750
912
|
'with(obj||{}){__p.push(\'' +
|
751
913
|
str.replace(/\\/g, '\\\\')
|
752
914
|
.replace(/'/g, "\\'")
|
753
|
-
.replace(c.
|
754
|
-
return "'," + code
|
915
|
+
.replace(c.escape || noMatch, function(match, code) {
|
916
|
+
return "',_.escape(" + unescape(code) + "),'";
|
917
|
+
})
|
918
|
+
.replace(c.interpolate || noMatch, function(match, code) {
|
919
|
+
return "'," + unescape(code) + ",'";
|
755
920
|
})
|
756
|
-
.replace(c.evaluate ||
|
757
|
-
return "');" + code.replace(
|
758
|
-
.replace(/[\r\n\t]/g, ' ') + "__p.push('";
|
921
|
+
.replace(c.evaluate || noMatch, function(match, code) {
|
922
|
+
return "');" + unescape(code).replace(/[\r\n\t]/g, ' ') + ";__p.push('";
|
759
923
|
})
|
760
924
|
.replace(/\r/g, '\\r')
|
761
925
|
.replace(/\n/g, '\\n')
|
762
926
|
.replace(/\t/g, '\\t')
|
763
927
|
+ "');}return __p.join('');";
|
764
|
-
var func = new Function('obj', tmpl);
|
765
|
-
|
928
|
+
var func = new Function('obj', '_', tmpl);
|
929
|
+
if (data) return func(data, _);
|
930
|
+
return function(data) {
|
931
|
+
return func.call(this, data, _);
|
932
|
+
};
|
933
|
+
};
|
934
|
+
|
935
|
+
// Add a "chain" function, which will delegate to the wrapper.
|
936
|
+
_.chain = function(obj) {
|
937
|
+
return _(obj).chain();
|
766
938
|
};
|
767
939
|
|
768
940
|
// The OOP Wrapper
|
@@ -797,8 +969,11 @@
|
|
797
969
|
each(['pop', 'push', 'reverse', 'shift', 'sort', 'splice', 'unshift'], function(name) {
|
798
970
|
var method = ArrayProto[name];
|
799
971
|
wrapper.prototype[name] = function() {
|
800
|
-
|
801
|
-
|
972
|
+
var wrapped = this._wrapped;
|
973
|
+
method.apply(wrapped, arguments);
|
974
|
+
var length = wrapped.length;
|
975
|
+
if ((name == 'shift' || name == 'splice') && length === 0) delete wrapped[0];
|
976
|
+
return result(wrapped, this._chain);
|
802
977
|
};
|
803
978
|
});
|
804
979
|
|
@@ -821,4 +996,4 @@
|
|
821
996
|
return this._wrapped;
|
822
997
|
};
|
823
998
|
|
824
|
-
})();
|
999
|
+
}).call(this);
|