overlay_me 0.12.1 → 0.13.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.md +4 -0
- data/Gemfile.lock +1 -1
- data/README.md +6 -4
- data/javascripts/basics.js.coffee +6 -2
- data/javascripts/lib/underscore.js +409 -157
- data/javascripts/menu.js.coffee +2 -1
- data/javascripts/mixins/hideable.js.coffee +17 -4
- data/javascripts/mixins/storable.js.coffee +4 -0
- data/javascripts/overlays/image.js.coffee +7 -5
- data/javascripts/overlays/images_container.js.coffee +52 -13
- data/javascripts/overlays/images_directory.js.coffee +46 -0
- data/javascripts/overlays/images_mngt_div.js.coffee +0 -27
- data/javascripts/overlays.js.coffee +40 -2
- data/lib/overlay_me/version.rb +2 -2
- data/lib/overlay_me.rb +1 -1
- data/overlay_me.css +85 -40
- data/overlay_me.js +699 -246
- data/stylesheets/overlay_me.css.scss +47 -7
- data/vendor/assets/javascripts/overlay_me/overlay_me.min.js +54 -28
- metadata +7 -9
- data/demo_page.html +0 -33
@@ -1,5 +1,5 @@
|
|
1
|
-
// Underscore.js 1.
|
2
|
-
// (c)
|
1
|
+
// Underscore.js 1.3.3
|
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,36 +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
|
-
root
|
61
|
+
root['_'] = _;
|
59
62
|
}
|
60
63
|
|
61
64
|
// Current version.
|
62
|
-
_.VERSION = '1.
|
65
|
+
_.VERSION = '1.3.3';
|
63
66
|
|
64
67
|
// Collection Functions
|
65
68
|
// --------------------
|
66
69
|
|
67
70
|
// The cornerstone, an `each` implementation, aka `forEach`.
|
68
|
-
// Handles objects
|
71
|
+
// Handles objects with the built-in `forEach`, arrays, and raw objects.
|
69
72
|
// Delegates to **ECMAScript 5**'s native `forEach` if available.
|
70
73
|
var each = _.each = _.forEach = function(obj, iterator, context) {
|
71
74
|
if (obj == null) return;
|
72
75
|
if (nativeForEach && obj.forEach === nativeForEach) {
|
73
76
|
obj.forEach(iterator, context);
|
74
|
-
} else if (
|
77
|
+
} else if (obj.length === +obj.length) {
|
75
78
|
for (var i = 0, l = obj.length; i < l; i++) {
|
76
|
-
if (iterator.call(context, obj[i], i, obj) === breaker) return;
|
79
|
+
if (i in obj && iterator.call(context, obj[i], i, obj) === breaker) return;
|
77
80
|
}
|
78
81
|
} else {
|
79
82
|
for (var key in obj) {
|
80
|
-
if (
|
83
|
+
if (_.has(obj, key)) {
|
81
84
|
if (iterator.call(context, obj[key], key, obj) === breaker) return;
|
82
85
|
}
|
83
86
|
}
|
@@ -86,47 +89,50 @@
|
|
86
89
|
|
87
90
|
// Return the results of applying the iterator to each element.
|
88
91
|
// Delegates to **ECMAScript 5**'s native `map` if available.
|
89
|
-
_.map = function(obj, iterator, context) {
|
92
|
+
_.map = _.collect = function(obj, iterator, context) {
|
90
93
|
var results = [];
|
91
94
|
if (obj == null) return results;
|
92
95
|
if (nativeMap && obj.map === nativeMap) return obj.map(iterator, context);
|
93
96
|
each(obj, function(value, index, list) {
|
94
97
|
results[results.length] = iterator.call(context, value, index, list);
|
95
98
|
});
|
99
|
+
if (obj.length === +obj.length) results.length = obj.length;
|
96
100
|
return results;
|
97
101
|
};
|
98
102
|
|
99
103
|
// **Reduce** builds up a single result from a list of values, aka `inject`,
|
100
104
|
// or `foldl`. Delegates to **ECMAScript 5**'s native `reduce` if available.
|
101
105
|
_.reduce = _.foldl = _.inject = function(obj, iterator, memo, context) {
|
102
|
-
var initial =
|
106
|
+
var initial = arguments.length > 2;
|
103
107
|
if (obj == null) obj = [];
|
104
108
|
if (nativeReduce && obj.reduce === nativeReduce) {
|
105
109
|
if (context) iterator = _.bind(iterator, context);
|
106
110
|
return initial ? obj.reduce(iterator, memo) : obj.reduce(iterator);
|
107
111
|
}
|
108
112
|
each(obj, function(value, index, list) {
|
109
|
-
if (!initial
|
113
|
+
if (!initial) {
|
110
114
|
memo = value;
|
111
115
|
initial = true;
|
112
116
|
} else {
|
113
117
|
memo = iterator.call(context, memo, value, index, list);
|
114
118
|
}
|
115
119
|
});
|
116
|
-
if (!initial) throw new TypeError(
|
120
|
+
if (!initial) throw new TypeError('Reduce of empty array with no initial value');
|
117
121
|
return memo;
|
118
122
|
};
|
119
123
|
|
120
124
|
// The right-associative version of reduce, also known as `foldr`.
|
121
125
|
// Delegates to **ECMAScript 5**'s native `reduceRight` if available.
|
122
126
|
_.reduceRight = _.foldr = function(obj, iterator, memo, context) {
|
127
|
+
var initial = arguments.length > 2;
|
123
128
|
if (obj == null) obj = [];
|
124
129
|
if (nativeReduceRight && obj.reduceRight === nativeReduceRight) {
|
125
130
|
if (context) iterator = _.bind(iterator, context);
|
126
|
-
return
|
131
|
+
return initial ? obj.reduceRight(iterator, memo) : obj.reduceRight(iterator);
|
127
132
|
}
|
128
|
-
var reversed =
|
129
|
-
|
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);
|
130
136
|
};
|
131
137
|
|
132
138
|
// Return the first value which passes a truth test. Aliased as `detect`.
|
@@ -174,7 +180,7 @@
|
|
174
180
|
each(obj, function(value, index, list) {
|
175
181
|
if (!(result = result && iterator.call(context, value, index, list))) return breaker;
|
176
182
|
});
|
177
|
-
return result;
|
183
|
+
return !!result;
|
178
184
|
};
|
179
185
|
|
180
186
|
// Determine if at least one element in the object matches a truth test.
|
@@ -186,9 +192,9 @@
|
|
186
192
|
if (obj == null) return result;
|
187
193
|
if (nativeSome && obj.some === nativeSome) return obj.some(iterator, context);
|
188
194
|
each(obj, function(value, index, list) {
|
189
|
-
if (result = iterator.call(context, value, index, list)) return breaker;
|
195
|
+
if (result || (result = iterator.call(context, value, index, list))) return breaker;
|
190
196
|
});
|
191
|
-
return result;
|
197
|
+
return !!result;
|
192
198
|
};
|
193
199
|
|
194
200
|
// Determine if a given value is included in the array or object using `===`.
|
@@ -197,8 +203,8 @@
|
|
197
203
|
var found = false;
|
198
204
|
if (obj == null) return found;
|
199
205
|
if (nativeIndexOf && obj.indexOf === nativeIndexOf) return obj.indexOf(target) != -1;
|
200
|
-
any(obj, function(value) {
|
201
|
-
|
206
|
+
found = any(obj, function(value) {
|
207
|
+
return value === target;
|
202
208
|
});
|
203
209
|
return found;
|
204
210
|
};
|
@@ -207,7 +213,7 @@
|
|
207
213
|
_.invoke = function(obj, method) {
|
208
214
|
var args = slice.call(arguments, 2);
|
209
215
|
return _.map(obj, function(value) {
|
210
|
-
return (method
|
216
|
+
return (_.isFunction(method) ? method || value : value[method]).apply(value, args);
|
211
217
|
});
|
212
218
|
};
|
213
219
|
|
@@ -218,7 +224,8 @@
|
|
218
224
|
|
219
225
|
// Return the maximum element or (element-based computation).
|
220
226
|
_.max = function(obj, iterator, context) {
|
221
|
-
if (!iterator && _.isArray(obj)) return Math.max.apply(Math, obj);
|
227
|
+
if (!iterator && _.isArray(obj) && obj[0] === +obj[0]) return Math.max.apply(Math, obj);
|
228
|
+
if (!iterator && _.isEmpty(obj)) return -Infinity;
|
222
229
|
var result = {computed : -Infinity};
|
223
230
|
each(obj, function(value, index, list) {
|
224
231
|
var computed = iterator ? iterator.call(context, value, index, list) : value;
|
@@ -229,7 +236,8 @@
|
|
229
236
|
|
230
237
|
// Return the minimum element (or element-based computation).
|
231
238
|
_.min = function(obj, iterator, context) {
|
232
|
-
if (!iterator && _.isArray(obj)) return Math.min.apply(Math, obj);
|
239
|
+
if (!iterator && _.isArray(obj) && obj[0] === +obj[0]) return Math.min.apply(Math, obj);
|
240
|
+
if (!iterator && _.isEmpty(obj)) return Infinity;
|
233
241
|
var result = {computed : Infinity};
|
234
242
|
each(obj, function(value, index, list) {
|
235
243
|
var computed = iterator ? iterator.call(context, value, index, list) : value;
|
@@ -238,8 +246,20 @@
|
|
238
246
|
return result.value;
|
239
247
|
};
|
240
248
|
|
249
|
+
// Shuffle an array.
|
250
|
+
_.shuffle = function(obj) {
|
251
|
+
var shuffled = [], rand;
|
252
|
+
each(obj, function(value, index, list) {
|
253
|
+
rand = Math.floor(Math.random() * (index + 1));
|
254
|
+
shuffled[index] = shuffled[rand];
|
255
|
+
shuffled[rand] = value;
|
256
|
+
});
|
257
|
+
return shuffled;
|
258
|
+
};
|
259
|
+
|
241
260
|
// Sort the object's values by a criterion produced by an iterator.
|
242
|
-
_.sortBy = function(obj,
|
261
|
+
_.sortBy = function(obj, val, context) {
|
262
|
+
var iterator = _.isFunction(val) ? val : function(obj) { return obj[val]; };
|
243
263
|
return _.pluck(_.map(obj, function(value, index, list) {
|
244
264
|
return {
|
245
265
|
value : value,
|
@@ -247,10 +267,24 @@
|
|
247
267
|
};
|
248
268
|
}).sort(function(left, right) {
|
249
269
|
var a = left.criteria, b = right.criteria;
|
270
|
+
if (a === void 0) return 1;
|
271
|
+
if (b === void 0) return -1;
|
250
272
|
return a < b ? -1 : a > b ? 1 : 0;
|
251
273
|
}), 'value');
|
252
274
|
};
|
253
275
|
|
276
|
+
// Groups the object's values by a criterion. Pass either a string attribute
|
277
|
+
// to group by, or a function that returns the criterion.
|
278
|
+
_.groupBy = function(obj, val) {
|
279
|
+
var result = {};
|
280
|
+
var iterator = _.isFunction(val) ? val : function(obj) { return obj[val]; };
|
281
|
+
each(obj, function(value, index) {
|
282
|
+
var key = iterator(value, index);
|
283
|
+
(result[key] || (result[key] = [])).push(value);
|
284
|
+
});
|
285
|
+
return result;
|
286
|
+
};
|
287
|
+
|
254
288
|
// Use a comparator function to figure out at what index an object should
|
255
289
|
// be inserted so as to maintain order. Uses binary search.
|
256
290
|
_.sortedIndex = function(array, obj, iterator) {
|
@@ -264,29 +298,47 @@
|
|
264
298
|
};
|
265
299
|
|
266
300
|
// Safely convert anything iterable into a real, live array.
|
267
|
-
_.toArray = function(
|
268
|
-
if (!
|
269
|
-
if (
|
270
|
-
if (_.
|
271
|
-
if (_.
|
272
|
-
return _.values(
|
301
|
+
_.toArray = function(obj) {
|
302
|
+
if (!obj) return [];
|
303
|
+
if (_.isArray(obj)) return slice.call(obj);
|
304
|
+
if (_.isArguments(obj)) return slice.call(obj);
|
305
|
+
if (obj.toArray && _.isFunction(obj.toArray)) return obj.toArray();
|
306
|
+
return _.values(obj);
|
273
307
|
};
|
274
308
|
|
275
309
|
// Return the number of elements in an object.
|
276
310
|
_.size = function(obj) {
|
277
|
-
return _.
|
311
|
+
return _.isArray(obj) ? obj.length : _.keys(obj).length;
|
278
312
|
};
|
279
313
|
|
280
314
|
// Array Functions
|
281
315
|
// ---------------
|
282
316
|
|
283
317
|
// Get the first element of an array. Passing **n** will return the first N
|
284
|
-
// values in the array. Aliased as `head`. The **guard** check
|
285
|
-
// with `_.map`.
|
286
|
-
_.first = _.head = function(array, n, guard) {
|
318
|
+
// values in the array. Aliased as `head` and `take`. The **guard** check
|
319
|
+
// allows it to work with `_.map`.
|
320
|
+
_.first = _.head = _.take = function(array, n, guard) {
|
287
321
|
return (n != null) && !guard ? slice.call(array, 0, n) : array[0];
|
288
322
|
};
|
289
323
|
|
324
|
+
// Returns everything but the last entry of the array. Especcialy useful on
|
325
|
+
// the arguments object. Passing **n** will return all the values in
|
326
|
+
// the array, excluding the last N. The **guard** check allows it to work with
|
327
|
+
// `_.map`.
|
328
|
+
_.initial = function(array, n, guard) {
|
329
|
+
return slice.call(array, 0, array.length - ((n == null) || guard ? 1 : n));
|
330
|
+
};
|
331
|
+
|
332
|
+
// Get the last element of an array. Passing **n** will return the last N
|
333
|
+
// values in the array. The **guard** check allows it to work with `_.map`.
|
334
|
+
_.last = function(array, n, guard) {
|
335
|
+
if ((n != null) && !guard) {
|
336
|
+
return slice.call(array, Math.max(array.length - n, 0));
|
337
|
+
} else {
|
338
|
+
return array[array.length - 1];
|
339
|
+
}
|
340
|
+
};
|
341
|
+
|
290
342
|
// Returns everything but the first entry of the array. Aliased as `tail`.
|
291
343
|
// Especially useful on the arguments object. Passing an **index** will return
|
292
344
|
// the rest of the values in the array from that index onward. The **guard**
|
@@ -295,20 +347,15 @@
|
|
295
347
|
return slice.call(array, (index == null) || guard ? 1 : index);
|
296
348
|
};
|
297
349
|
|
298
|
-
// Get the last element of an array.
|
299
|
-
_.last = function(array) {
|
300
|
-
return array[array.length - 1];
|
301
|
-
};
|
302
|
-
|
303
350
|
// Trim out all falsy values from an array.
|
304
351
|
_.compact = function(array) {
|
305
352
|
return _.filter(array, function(value){ return !!value; });
|
306
353
|
};
|
307
354
|
|
308
355
|
// Return a completely flattened version of an array.
|
309
|
-
_.flatten = function(array) {
|
356
|
+
_.flatten = function(array, shallow) {
|
310
357
|
return _.reduce(array, function(memo, value) {
|
311
|
-
if (_.isArray(value)) return memo.concat(_.flatten(value));
|
358
|
+
if (_.isArray(value)) return memo.concat(shallow ? value : _.flatten(value));
|
312
359
|
memo[memo.length] = value;
|
313
360
|
return memo;
|
314
361
|
}, []);
|
@@ -316,23 +363,36 @@
|
|
316
363
|
|
317
364
|
// Return a version of the array that does not contain the specified value(s).
|
318
365
|
_.without = function(array) {
|
319
|
-
|
320
|
-
return _.filter(array, function(value){ return !_.include(values, value); });
|
366
|
+
return _.difference(array, slice.call(arguments, 1));
|
321
367
|
};
|
322
368
|
|
323
369
|
// Produce a duplicate-free version of the array. If the array has already
|
324
370
|
// been sorted, you have the option of using a faster algorithm.
|
325
371
|
// Aliased as `unique`.
|
326
|
-
_.uniq = _.unique = function(array, isSorted) {
|
327
|
-
|
328
|
-
|
372
|
+
_.uniq = _.unique = function(array, isSorted, iterator) {
|
373
|
+
var initial = iterator ? _.map(array, iterator) : array;
|
374
|
+
var results = [];
|
375
|
+
// The `isSorted` flag is irrelevant if the array only contains two elements.
|
376
|
+
if (array.length < 3) isSorted = true;
|
377
|
+
_.reduce(initial, function (memo, value, index) {
|
378
|
+
if (isSorted ? _.last(memo) !== value || !memo.length : !_.include(memo, value)) {
|
379
|
+
memo.push(value);
|
380
|
+
results.push(array[index]);
|
381
|
+
}
|
329
382
|
return memo;
|
330
383
|
}, []);
|
384
|
+
return results;
|
385
|
+
};
|
386
|
+
|
387
|
+
// Produce an array that contains the union: each distinct element from all of
|
388
|
+
// the passed-in arrays.
|
389
|
+
_.union = function() {
|
390
|
+
return _.uniq(_.flatten(arguments, true));
|
331
391
|
};
|
332
392
|
|
333
393
|
// Produce an array that contains every item shared between all the
|
334
|
-
// passed-in arrays.
|
335
|
-
_.intersect = function(array) {
|
394
|
+
// passed-in arrays. (Aliased as "intersect" for back-compat.)
|
395
|
+
_.intersection = _.intersect = function(array) {
|
336
396
|
var rest = slice.call(arguments, 1);
|
337
397
|
return _.filter(_.uniq(array), function(item) {
|
338
398
|
return _.every(rest, function(other) {
|
@@ -341,6 +401,13 @@
|
|
341
401
|
});
|
342
402
|
};
|
343
403
|
|
404
|
+
// Take the difference between one array and a number of other arrays.
|
405
|
+
// Only the elements present in just the first array will remain.
|
406
|
+
_.difference = function(array) {
|
407
|
+
var rest = _.flatten(slice.call(arguments, 1), true);
|
408
|
+
return _.filter(array, function(value){ return !_.include(rest, value); });
|
409
|
+
};
|
410
|
+
|
344
411
|
// Zip together multiple lists into a single array -- elements that share
|
345
412
|
// an index go together.
|
346
413
|
_.zip = function() {
|
@@ -365,17 +432,16 @@
|
|
365
432
|
return array[i] === item ? i : -1;
|
366
433
|
}
|
367
434
|
if (nativeIndexOf && array.indexOf === nativeIndexOf) return array.indexOf(item);
|
368
|
-
for (i = 0, l = array.length; i < l; i++) if (array[i] === item) return i;
|
435
|
+
for (i = 0, l = array.length; i < l; i++) if (i in array && array[i] === item) return i;
|
369
436
|
return -1;
|
370
437
|
};
|
371
438
|
|
372
|
-
|
373
439
|
// Delegates to **ECMAScript 5**'s native `lastIndexOf` if available.
|
374
440
|
_.lastIndexOf = function(array, item) {
|
375
441
|
if (array == null) return -1;
|
376
442
|
if (nativeLastIndexOf && array.lastIndexOf === nativeLastIndexOf) return array.lastIndexOf(item);
|
377
443
|
var i = array.length;
|
378
|
-
while (i--) if (array[i] === item) return i;
|
444
|
+
while (i--) if (i in array && array[i] === item) return i;
|
379
445
|
return -1;
|
380
446
|
};
|
381
447
|
|
@@ -404,15 +470,25 @@
|
|
404
470
|
// Function (ahem) Functions
|
405
471
|
// ------------------
|
406
472
|
|
473
|
+
// Reusable constructor function for prototype setting.
|
474
|
+
var ctor = function(){};
|
475
|
+
|
407
476
|
// Create a function bound to a given object (assigning `this`, and arguments,
|
408
477
|
// optionally). Binding with arguments is also known as `curry`.
|
409
478
|
// Delegates to **ECMAScript 5**'s native `Function.bind` if available.
|
410
479
|
// We check for `func.bind` first, to fail fast when `func` is undefined.
|
411
|
-
_.bind = function(func,
|
480
|
+
_.bind = function bind(func, context) {
|
481
|
+
var bound, args;
|
412
482
|
if (func.bind === nativeBind && nativeBind) return nativeBind.apply(func, slice.call(arguments, 1));
|
413
|
-
|
414
|
-
|
415
|
-
|
483
|
+
if (!_.isFunction(func)) throw new TypeError;
|
484
|
+
args = slice.call(arguments, 2);
|
485
|
+
return bound = function() {
|
486
|
+
if (!(this instanceof bound)) return func.apply(context, args.concat(slice.call(arguments)));
|
487
|
+
ctor.prototype = func.prototype;
|
488
|
+
var self = new ctor;
|
489
|
+
var result = func.apply(self, args.concat(slice.call(arguments)));
|
490
|
+
if (Object(result) === result) return result;
|
491
|
+
return self;
|
416
492
|
};
|
417
493
|
};
|
418
494
|
|
@@ -431,7 +507,7 @@
|
|
431
507
|
hasher || (hasher = _.identity);
|
432
508
|
return function() {
|
433
509
|
var key = hasher.apply(this, arguments);
|
434
|
-
return
|
510
|
+
return _.has(memo, key) ? memo[key] : (memo[key] = func.apply(this, arguments));
|
435
511
|
};
|
436
512
|
};
|
437
513
|
|
@@ -439,7 +515,7 @@
|
|
439
515
|
// it with the arguments supplied.
|
440
516
|
_.delay = function(func, wait) {
|
441
517
|
var args = slice.call(arguments, 2);
|
442
|
-
return setTimeout(function(){ return func.apply(
|
518
|
+
return setTimeout(function(){ return func.apply(null, args); }, wait);
|
443
519
|
};
|
444
520
|
|
445
521
|
// Defers a function, scheduling it to run after the current call stack has
|
@@ -448,31 +524,46 @@
|
|
448
524
|
return _.delay.apply(_, [func, 1].concat(slice.call(arguments, 1)));
|
449
525
|
};
|
450
526
|
|
451
|
-
//
|
452
|
-
|
453
|
-
|
527
|
+
// Returns a function, that, when invoked, will only be triggered at most once
|
528
|
+
// during a given window of time.
|
529
|
+
_.throttle = function(func, wait) {
|
530
|
+
var context, args, timeout, throttling, more, result;
|
531
|
+
var whenDone = _.debounce(function(){ more = throttling = false; }, wait);
|
454
532
|
return function() {
|
455
|
-
|
456
|
-
var
|
533
|
+
context = this; args = arguments;
|
534
|
+
var later = function() {
|
457
535
|
timeout = null;
|
458
|
-
func.apply(context, args);
|
536
|
+
if (more) func.apply(context, args);
|
537
|
+
whenDone();
|
459
538
|
};
|
460
|
-
if (
|
461
|
-
if (
|
539
|
+
if (!timeout) timeout = setTimeout(later, wait);
|
540
|
+
if (throttling) {
|
541
|
+
more = true;
|
542
|
+
} else {
|
543
|
+
result = func.apply(context, args);
|
544
|
+
}
|
545
|
+
whenDone();
|
546
|
+
throttling = true;
|
547
|
+
return result;
|
462
548
|
};
|
463
549
|
};
|
464
550
|
|
465
|
-
// Returns a function, that, when invoked, will only be triggered at most once
|
466
|
-
// during a given window of time.
|
467
|
-
_.throttle = function(func, wait) {
|
468
|
-
return limit(func, wait, false);
|
469
|
-
};
|
470
|
-
|
471
551
|
// Returns a function, that, as long as it continues to be invoked, will not
|
472
552
|
// be triggered. The function will be called after it stops being called for
|
473
|
-
// N milliseconds.
|
474
|
-
|
475
|
-
|
553
|
+
// N milliseconds. If `immediate` is passed, trigger the function on the
|
554
|
+
// leading edge, instead of the trailing.
|
555
|
+
_.debounce = function(func, wait, immediate) {
|
556
|
+
var timeout;
|
557
|
+
return function() {
|
558
|
+
var context = this, args = arguments;
|
559
|
+
var later = function() {
|
560
|
+
timeout = null;
|
561
|
+
if (!immediate) func.apply(context, args);
|
562
|
+
};
|
563
|
+
if (immediate && !timeout) func.apply(context, args);
|
564
|
+
clearTimeout(timeout);
|
565
|
+
timeout = setTimeout(later, wait);
|
566
|
+
};
|
476
567
|
};
|
477
568
|
|
478
569
|
// Returns a function that will be executed at most one time, no matter how
|
@@ -491,7 +582,7 @@
|
|
491
582
|
// conditionally execute the original function.
|
492
583
|
_.wrap = function(func, wrapper) {
|
493
584
|
return function() {
|
494
|
-
var args = [func].concat(slice.call(arguments));
|
585
|
+
var args = [func].concat(slice.call(arguments, 0));
|
495
586
|
return wrapper.apply(this, args);
|
496
587
|
};
|
497
588
|
};
|
@@ -499,10 +590,10 @@
|
|
499
590
|
// Returns a function that is the composition of a list of functions, each
|
500
591
|
// consuming the return value of the function that follows.
|
501
592
|
_.compose = function() {
|
502
|
-
var funcs =
|
593
|
+
var funcs = arguments;
|
503
594
|
return function() {
|
504
|
-
var args =
|
505
|
-
for (var i=funcs.length-1; i >= 0; i--) {
|
595
|
+
var args = arguments;
|
596
|
+
for (var i = funcs.length - 1; i >= 0; i--) {
|
506
597
|
args = [funcs[i].apply(this, args)];
|
507
598
|
}
|
508
599
|
return args[0];
|
@@ -511,12 +602,12 @@
|
|
511
602
|
|
512
603
|
// Returns a function that will only be executed after being called N times.
|
513
604
|
_.after = function(times, func) {
|
605
|
+
if (times <= 0) return func();
|
514
606
|
return function() {
|
515
607
|
if (--times < 1) { return func.apply(this, arguments); }
|
516
608
|
};
|
517
609
|
};
|
518
610
|
|
519
|
-
|
520
611
|
// Object Functions
|
521
612
|
// ----------------
|
522
613
|
|
@@ -525,7 +616,7 @@
|
|
525
616
|
_.keys = nativeKeys || function(obj) {
|
526
617
|
if (obj !== Object(obj)) throw new TypeError('Invalid object');
|
527
618
|
var keys = [];
|
528
|
-
for (var key in obj) if (
|
619
|
+
for (var key in obj) if (_.has(obj, key)) keys[keys.length] = key;
|
529
620
|
return keys;
|
530
621
|
};
|
531
622
|
|
@@ -537,19 +628,32 @@
|
|
537
628
|
// Return a sorted list of the function names available on the object.
|
538
629
|
// Aliased as `methods`
|
539
630
|
_.functions = _.methods = function(obj) {
|
540
|
-
|
631
|
+
var names = [];
|
632
|
+
for (var key in obj) {
|
633
|
+
if (_.isFunction(obj[key])) names.push(key);
|
634
|
+
}
|
635
|
+
return names.sort();
|
541
636
|
};
|
542
637
|
|
543
638
|
// Extend a given object with all the properties in passed-in object(s).
|
544
639
|
_.extend = function(obj) {
|
545
640
|
each(slice.call(arguments, 1), function(source) {
|
546
641
|
for (var prop in source) {
|
547
|
-
|
642
|
+
obj[prop] = source[prop];
|
548
643
|
}
|
549
644
|
});
|
550
645
|
return obj;
|
551
646
|
};
|
552
647
|
|
648
|
+
// Return a copy of the object only containing the whitelisted properties.
|
649
|
+
_.pick = function(obj) {
|
650
|
+
var result = {};
|
651
|
+
each(_.flatten(slice.call(arguments, 1)), function(key) {
|
652
|
+
if (key in obj) result[key] = obj[key];
|
653
|
+
});
|
654
|
+
return result;
|
655
|
+
};
|
656
|
+
|
553
657
|
// Fill in a given object with default properties.
|
554
658
|
_.defaults = function(obj) {
|
555
659
|
each(slice.call(arguments, 1), function(source) {
|
@@ -562,6 +666,7 @@
|
|
562
666
|
|
563
667
|
// Create a (shallow-cloned) duplicate of an object.
|
564
668
|
_.clone = function(obj) {
|
669
|
+
if (!_.isObject(obj)) return obj;
|
565
670
|
return _.isArray(obj) ? obj.slice() : _.extend({}, obj);
|
566
671
|
};
|
567
672
|
|
@@ -573,49 +678,105 @@
|
|
573
678
|
return obj;
|
574
679
|
};
|
575
680
|
|
576
|
-
//
|
577
|
-
|
578
|
-
//
|
579
|
-
|
580
|
-
|
581
|
-
|
582
|
-
if (
|
583
|
-
// Basic equality test (watch out for coercions).
|
584
|
-
if (a == b) return true;
|
585
|
-
// One is falsy and the other truthy.
|
586
|
-
if ((!a && b) || (a && !b)) return false;
|
681
|
+
// Internal recursive comparison function.
|
682
|
+
function eq(a, b, stack) {
|
683
|
+
// Identical objects are equal. `0 === -0`, but they aren't identical.
|
684
|
+
// See the Harmony `egal` proposal: http://wiki.ecmascript.org/doku.php?id=harmony:egal.
|
685
|
+
if (a === b) return a !== 0 || 1 / a == 1 / b;
|
686
|
+
// A strict comparison is necessary because `null == undefined`.
|
687
|
+
if (a == null || b == null) return a === b;
|
587
688
|
// Unwrap any wrapped objects.
|
588
689
|
if (a._chain) a = a._wrapped;
|
589
690
|
if (b._chain) b = b._wrapped;
|
590
|
-
//
|
591
|
-
if (a.isEqual) return a.isEqual(b);
|
592
|
-
|
593
|
-
|
594
|
-
|
595
|
-
if (
|
596
|
-
|
597
|
-
|
598
|
-
|
599
|
-
|
600
|
-
|
601
|
-
|
602
|
-
|
603
|
-
|
604
|
-
|
605
|
-
|
606
|
-
|
607
|
-
|
608
|
-
|
609
|
-
|
610
|
-
|
611
|
-
|
612
|
-
|
691
|
+
// Invoke a custom `isEqual` method if one is provided.
|
692
|
+
if (a.isEqual && _.isFunction(a.isEqual)) return a.isEqual(b);
|
693
|
+
if (b.isEqual && _.isFunction(b.isEqual)) return b.isEqual(a);
|
694
|
+
// Compare `[[Class]]` names.
|
695
|
+
var className = toString.call(a);
|
696
|
+
if (className != toString.call(b)) return false;
|
697
|
+
switch (className) {
|
698
|
+
// Strings, numbers, dates, and booleans are compared by value.
|
699
|
+
case '[object String]':
|
700
|
+
// Primitives and their corresponding object wrappers are equivalent; thus, `"5"` is
|
701
|
+
// equivalent to `new String("5")`.
|
702
|
+
return a == String(b);
|
703
|
+
case '[object Number]':
|
704
|
+
// `NaN`s are equivalent, but non-reflexive. An `egal` comparison is performed for
|
705
|
+
// other numeric values.
|
706
|
+
return a != +a ? b != +b : (a == 0 ? 1 / a == 1 / b : a == +b);
|
707
|
+
case '[object Date]':
|
708
|
+
case '[object Boolean]':
|
709
|
+
// Coerce dates and booleans to numeric primitive values. Dates are compared by their
|
710
|
+
// millisecond representations. Note that invalid dates with millisecond representations
|
711
|
+
// of `NaN` are not equivalent.
|
712
|
+
return +a == +b;
|
713
|
+
// RegExps are compared by their source patterns and flags.
|
714
|
+
case '[object RegExp]':
|
715
|
+
return a.source == b.source &&
|
716
|
+
a.global == b.global &&
|
717
|
+
a.multiline == b.multiline &&
|
718
|
+
a.ignoreCase == b.ignoreCase;
|
719
|
+
}
|
720
|
+
if (typeof a != 'object' || typeof b != 'object') return false;
|
721
|
+
// Assume equality for cyclic structures. The algorithm for detecting cyclic
|
722
|
+
// structures is adapted from ES 5.1 section 15.12.3, abstract operation `JO`.
|
723
|
+
var length = stack.length;
|
724
|
+
while (length--) {
|
725
|
+
// Linear search. Performance is inversely proportional to the number of
|
726
|
+
// unique nested structures.
|
727
|
+
if (stack[length] == a) return true;
|
728
|
+
}
|
729
|
+
// Add the first object to the stack of traversed objects.
|
730
|
+
stack.push(a);
|
731
|
+
var size = 0, result = true;
|
732
|
+
// Recursively compare objects and arrays.
|
733
|
+
if (className == '[object Array]') {
|
734
|
+
// Compare array lengths to determine if a deep comparison is necessary.
|
735
|
+
size = a.length;
|
736
|
+
result = size == b.length;
|
737
|
+
if (result) {
|
738
|
+
// Deep compare the contents, ignoring non-numeric properties.
|
739
|
+
while (size--) {
|
740
|
+
// Ensure commutative equality for sparse arrays.
|
741
|
+
if (!(result = size in a == size in b && eq(a[size], b[size], stack))) break;
|
742
|
+
}
|
743
|
+
}
|
744
|
+
} else {
|
745
|
+
// Objects with different constructors are not equivalent.
|
746
|
+
if ('constructor' in a != 'constructor' in b || a.constructor != b.constructor) return false;
|
747
|
+
// Deep compare objects.
|
748
|
+
for (var key in a) {
|
749
|
+
if (_.has(a, key)) {
|
750
|
+
// Count the expected number of properties.
|
751
|
+
size++;
|
752
|
+
// Deep compare each member.
|
753
|
+
if (!(result = _.has(b, key) && eq(a[key], b[key], stack))) break;
|
754
|
+
}
|
755
|
+
}
|
756
|
+
// Ensure that both objects contain the same number of properties.
|
757
|
+
if (result) {
|
758
|
+
for (key in b) {
|
759
|
+
if (_.has(b, key) && !(size--)) break;
|
760
|
+
}
|
761
|
+
result = !size;
|
762
|
+
}
|
763
|
+
}
|
764
|
+
// Remove the first object from the stack of traversed objects.
|
765
|
+
stack.pop();
|
766
|
+
return result;
|
767
|
+
}
|
768
|
+
|
769
|
+
// Perform a deep comparison to check if two objects are equal.
|
770
|
+
_.isEqual = function(a, b) {
|
771
|
+
return eq(a, b, []);
|
613
772
|
};
|
614
773
|
|
615
|
-
// Is a given array or object empty?
|
774
|
+
// Is a given array, string, or object empty?
|
775
|
+
// An "empty" object has no enumerable own-properties.
|
616
776
|
_.isEmpty = function(obj) {
|
777
|
+
if (obj == null) return true;
|
617
778
|
if (_.isArray(obj) || _.isString(obj)) return obj.length === 0;
|
618
|
-
for (var key in obj) if (
|
779
|
+
for (var key in obj) if (_.has(obj, key)) return false;
|
619
780
|
return true;
|
620
781
|
};
|
621
782
|
|
@@ -627,48 +788,63 @@
|
|
627
788
|
// Is a given value an array?
|
628
789
|
// Delegates to ECMA5's native Array.isArray
|
629
790
|
_.isArray = nativeIsArray || function(obj) {
|
630
|
-
return toString.call(obj)
|
791
|
+
return toString.call(obj) == '[object Array]';
|
792
|
+
};
|
793
|
+
|
794
|
+
// Is a given variable an object?
|
795
|
+
_.isObject = function(obj) {
|
796
|
+
return obj === Object(obj);
|
631
797
|
};
|
632
798
|
|
633
799
|
// Is a given variable an arguments object?
|
634
800
|
_.isArguments = function(obj) {
|
635
|
-
return
|
801
|
+
return toString.call(obj) == '[object Arguments]';
|
636
802
|
};
|
803
|
+
if (!_.isArguments(arguments)) {
|
804
|
+
_.isArguments = function(obj) {
|
805
|
+
return !!(obj && _.has(obj, 'callee'));
|
806
|
+
};
|
807
|
+
}
|
637
808
|
|
638
809
|
// Is a given value a function?
|
639
810
|
_.isFunction = function(obj) {
|
640
|
-
return
|
811
|
+
return toString.call(obj) == '[object Function]';
|
641
812
|
};
|
642
813
|
|
643
814
|
// Is a given value a string?
|
644
815
|
_.isString = function(obj) {
|
645
|
-
return
|
816
|
+
return toString.call(obj) == '[object String]';
|
646
817
|
};
|
647
818
|
|
648
819
|
// Is a given value a number?
|
649
820
|
_.isNumber = function(obj) {
|
650
|
-
return
|
821
|
+
return toString.call(obj) == '[object Number]';
|
651
822
|
};
|
652
823
|
|
653
|
-
// Is
|
654
|
-
|
824
|
+
// Is a given object a finite number?
|
825
|
+
_.isFinite = function(obj) {
|
826
|
+
return _.isNumber(obj) && isFinite(obj);
|
827
|
+
};
|
828
|
+
|
829
|
+
// Is the given value `NaN`?
|
655
830
|
_.isNaN = function(obj) {
|
831
|
+
// `NaN` is the only value for which `===` is not reflexive.
|
656
832
|
return obj !== obj;
|
657
833
|
};
|
658
834
|
|
659
835
|
// Is a given value a boolean?
|
660
836
|
_.isBoolean = function(obj) {
|
661
|
-
return obj === true || obj === false;
|
837
|
+
return obj === true || obj === false || toString.call(obj) == '[object Boolean]';
|
662
838
|
};
|
663
839
|
|
664
840
|
// Is a given value a date?
|
665
841
|
_.isDate = function(obj) {
|
666
|
-
return
|
842
|
+
return toString.call(obj) == '[object Date]';
|
667
843
|
};
|
668
844
|
|
669
845
|
// Is the given value a regular expression?
|
670
846
|
_.isRegExp = function(obj) {
|
671
|
-
return
|
847
|
+
return toString.call(obj) == '[object RegExp]';
|
672
848
|
};
|
673
849
|
|
674
850
|
// Is a given value equal to null?
|
@@ -681,6 +857,11 @@
|
|
681
857
|
return obj === void 0;
|
682
858
|
};
|
683
859
|
|
860
|
+
// Has own property?
|
861
|
+
_.has = function(obj, key) {
|
862
|
+
return hasOwnProperty.call(obj, key);
|
863
|
+
};
|
864
|
+
|
684
865
|
// Utility Functions
|
685
866
|
// -----------------
|
686
867
|
|
@@ -701,6 +882,19 @@
|
|
701
882
|
for (var i = 0; i < n; i++) iterator.call(context, i);
|
702
883
|
};
|
703
884
|
|
885
|
+
// Escape a string for HTML interpolation.
|
886
|
+
_.escape = function(string) {
|
887
|
+
return (''+string).replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>').replace(/"/g, '"').replace(/'/g, ''').replace(/\//g,'/');
|
888
|
+
};
|
889
|
+
|
890
|
+
// If the value of the named property is a function then invoke it;
|
891
|
+
// otherwise, return it.
|
892
|
+
_.result = function(object, property) {
|
893
|
+
if (object == null) return null;
|
894
|
+
var value = object[property];
|
895
|
+
return _.isFunction(value) ? value.call(object) : value;
|
896
|
+
};
|
897
|
+
|
704
898
|
// Add your own custom functions to the Underscore object, ensuring that
|
705
899
|
// they're correctly added to the OOP wrapper as well.
|
706
900
|
_.mixin = function(obj) {
|
@@ -721,31 +915,86 @@
|
|
721
915
|
// following template settings to use alternative delimiters.
|
722
916
|
_.templateSettings = {
|
723
917
|
evaluate : /<%([\s\S]+?)%>/g,
|
724
|
-
interpolate : /<%=([\s\S]+?)%>/g
|
918
|
+
interpolate : /<%=([\s\S]+?)%>/g,
|
919
|
+
escape : /<%-([\s\S]+?)%>/g
|
920
|
+
};
|
921
|
+
|
922
|
+
// When customizing `templateSettings`, if you don't want to define an
|
923
|
+
// interpolation, evaluation or escaping regex, we need one that is
|
924
|
+
// guaranteed not to match.
|
925
|
+
var noMatch = /.^/;
|
926
|
+
|
927
|
+
// Certain characters need to be escaped so that they can be put into a
|
928
|
+
// string literal.
|
929
|
+
var escapes = {
|
930
|
+
'\\': '\\',
|
931
|
+
"'": "'",
|
932
|
+
'r': '\r',
|
933
|
+
'n': '\n',
|
934
|
+
't': '\t',
|
935
|
+
'u2028': '\u2028',
|
936
|
+
'u2029': '\u2029'
|
937
|
+
};
|
938
|
+
|
939
|
+
for (var p in escapes) escapes[escapes[p]] = p;
|
940
|
+
var escaper = /\\|'|\r|\n|\t|\u2028|\u2029/g;
|
941
|
+
var unescaper = /\\(\\|'|r|n|t|u2028|u2029)/g;
|
942
|
+
|
943
|
+
// Within an interpolation, evaluation, or escaping, remove HTML escaping
|
944
|
+
// that had been previously added.
|
945
|
+
var unescape = function(code) {
|
946
|
+
return code.replace(unescaper, function(match, escape) {
|
947
|
+
return escapes[escape];
|
948
|
+
});
|
725
949
|
};
|
726
950
|
|
727
951
|
// JavaScript micro-templating, similar to John Resig's implementation.
|
728
952
|
// Underscore templating handles arbitrary delimiters, preserves whitespace,
|
729
953
|
// and correctly escapes quotes within interpolated code.
|
730
|
-
_.template = function(
|
731
|
-
|
732
|
-
|
733
|
-
|
734
|
-
|
735
|
-
|
736
|
-
|
737
|
-
|
738
|
-
|
739
|
-
|
740
|
-
|
741
|
-
|
742
|
-
|
743
|
-
|
744
|
-
|
745
|
-
|
746
|
-
|
747
|
-
|
748
|
-
|
954
|
+
_.template = function(text, data, settings) {
|
955
|
+
settings = _.defaults(settings || {}, _.templateSettings);
|
956
|
+
|
957
|
+
// Compile the template source, taking care to escape characters that
|
958
|
+
// cannot be included in a string literal and then unescape them in code
|
959
|
+
// blocks.
|
960
|
+
var source = "__p+='" + text
|
961
|
+
.replace(escaper, function(match) {
|
962
|
+
return '\\' + escapes[match];
|
963
|
+
})
|
964
|
+
.replace(settings.escape || noMatch, function(match, code) {
|
965
|
+
return "'+\n_.escape(" + unescape(code) + ")+\n'";
|
966
|
+
})
|
967
|
+
.replace(settings.interpolate || noMatch, function(match, code) {
|
968
|
+
return "'+\n(" + unescape(code) + ")+\n'";
|
969
|
+
})
|
970
|
+
.replace(settings.evaluate || noMatch, function(match, code) {
|
971
|
+
return "';\n" + unescape(code) + "\n;__p+='";
|
972
|
+
}) + "';\n";
|
973
|
+
|
974
|
+
// If a variable is not specified, place data values in local scope.
|
975
|
+
if (!settings.variable) source = 'with(obj||{}){\n' + source + '}\n';
|
976
|
+
|
977
|
+
source = "var __p='';" +
|
978
|
+
"var print=function(){__p+=Array.prototype.join.call(arguments, '')};\n" +
|
979
|
+
source + "return __p;\n";
|
980
|
+
|
981
|
+
var render = new Function(settings.variable || 'obj', '_', source);
|
982
|
+
if (data) return render(data, _);
|
983
|
+
var template = function(data) {
|
984
|
+
return render.call(this, data, _);
|
985
|
+
};
|
986
|
+
|
987
|
+
// Provide the compiled function source as a convenience for build time
|
988
|
+
// precompilation.
|
989
|
+
template.source = 'function(' + (settings.variable || 'obj') + '){\n' +
|
990
|
+
source + '}';
|
991
|
+
|
992
|
+
return template;
|
993
|
+
};
|
994
|
+
|
995
|
+
// Add a "chain" function, which will delegate to the wrapper.
|
996
|
+
_.chain = function(obj) {
|
997
|
+
return _(obj).chain();
|
749
998
|
};
|
750
999
|
|
751
1000
|
// The OOP Wrapper
|
@@ -780,8 +1029,11 @@
|
|
780
1029
|
each(['pop', 'push', 'reverse', 'shift', 'sort', 'splice', 'unshift'], function(name) {
|
781
1030
|
var method = ArrayProto[name];
|
782
1031
|
wrapper.prototype[name] = function() {
|
783
|
-
|
784
|
-
|
1032
|
+
var wrapped = this._wrapped;
|
1033
|
+
method.apply(wrapped, arguments);
|
1034
|
+
var length = wrapped.length;
|
1035
|
+
if ((name == 'shift' || name == 'splice') && length === 0) delete wrapped[0];
|
1036
|
+
return result(wrapped, this._chain);
|
785
1037
|
};
|
786
1038
|
});
|
787
1039
|
|
@@ -804,4 +1056,4 @@
|
|
804
1056
|
return this._wrapped;
|
805
1057
|
};
|
806
1058
|
|
807
|
-
})();
|
1059
|
+
}).call(this);
|