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