rails-backbone 0.9.10 → 1.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/MIT-LICENSE +1 -1
- data/README.md +67 -21
- data/Rakefile +1 -0
- data/lib/generators/backbone/install/install_generator.rb +25 -3
- data/lib/generators/backbone/scaffold/templates/router.coffee +1 -1
- data/lib/generators/backbone/scaffold/templates/templates/model.jst +3 -3
- data/lib/generators/backbone/scaffold/templates/views/index_view.coffee +3 -3
- data/vendor/assets/javascripts/backbone.js +1076 -706
- data/vendor/assets/javascripts/backbone_rails_sync.js +21 -70
- data/vendor/assets/javascripts/underscore.js +901 -574
- metadata +52 -93
@@ -1,81 +1,32 @@
|
|
1
1
|
(function($) {
|
2
|
-
|
3
|
-
'create': 'POST',
|
4
|
-
'update': 'PUT',
|
5
|
-
'delete': 'DELETE',
|
6
|
-
'read' : 'GET'
|
7
|
-
};
|
8
|
-
|
9
|
-
var getUrl = function(object) {
|
10
|
-
if (!(object && object.url)) return null;
|
11
|
-
return _.isFunction(object.url) ? object.url() : object.url;
|
12
|
-
};
|
13
|
-
|
14
|
-
var urlError = function() {
|
15
|
-
throw new Error("A 'url' property or function must be specified");
|
16
|
-
};
|
2
|
+
Backbone._sync = Backbone.sync;
|
17
3
|
|
18
4
|
Backbone.sync = function(method, model, options) {
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
if (
|
27
|
-
|
28
|
-
if (token) xhr.setRequestHeader('X-CSRF-Token', token);
|
29
|
-
}
|
30
|
-
model.trigger('sync:start');
|
31
|
-
}
|
32
|
-
}, options);
|
33
|
-
|
34
|
-
if (!params.url) {
|
35
|
-
params.url = getUrl(model) || urlError();
|
5
|
+
if (!options.noCSRF) {
|
6
|
+
var beforeSend = options.beforeSend;
|
7
|
+
|
8
|
+
// Set X-CSRF-Token HTTP header
|
9
|
+
options.beforeSend = function(xhr) {
|
10
|
+
var token = $('meta[name="csrf-token"]').attr('content');
|
11
|
+
if (token) xhr.setRequestHeader('X-CSRF-Token', token);
|
12
|
+
if (beforeSend) return beforeSend.apply(this, arguments);
|
13
|
+
};
|
36
14
|
}
|
37
15
|
|
38
|
-
//
|
39
|
-
if (
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
data[model.paramRoot] = model.toJSON();
|
16
|
+
// Serialize data, optionally using paramRoot
|
17
|
+
if (options.data == null && model && (method === 'create' || method === 'update' || method === 'patch')) {
|
18
|
+
options.contentType = 'application/json';
|
19
|
+
data = JSON.stringify(options.attrs || model.toJSON(options));
|
20
|
+
if (model.paramRoot) {
|
21
|
+
data = {};
|
22
|
+
data[model.paramRoot] = model.toJSON(options);
|
46
23
|
} else {
|
47
24
|
data = model.toJSON();
|
48
25
|
}
|
49
|
-
|
50
|
-
params.data = JSON.stringify(data)
|
26
|
+
options.data = JSON.stringify(data);
|
51
27
|
}
|
52
28
|
|
53
|
-
|
54
|
-
|
55
|
-
params.processData = false;
|
56
|
-
}
|
57
|
-
|
58
|
-
// Trigger the sync end event
|
59
|
-
var complete = options.complete;
|
60
|
-
params.complete = function(jqXHR, textStatus) {
|
61
|
-
model.trigger('sync:end');
|
62
|
-
if (complete) complete(jqXHR, textStatus);
|
63
|
-
};
|
64
|
-
|
65
|
-
var success = options.success;
|
66
|
-
params.success = function(resp) {
|
67
|
-
if (success) success(model, resp, options);
|
68
|
-
model.trigger('sync', model, resp, options);
|
69
|
-
};
|
29
|
+
return Backbone._sync(method, model, options);
|
30
|
+
};
|
70
31
|
|
71
|
-
|
72
|
-
params.error = function(xhr) {
|
73
|
-
if (error) error(model, xhr, options);
|
74
|
-
model.trigger('error', model, xhr, options);
|
75
|
-
};
|
76
|
-
|
77
|
-
// Make the request.
|
78
|
-
return $.ajax(params);
|
79
|
-
}
|
80
|
-
|
81
|
-
})(jQuery);
|
32
|
+
})(jQuery);
|
@@ -1,6 +1,6 @@
|
|
1
|
-
// Underscore.js 1.
|
1
|
+
// Underscore.js 1.8.3
|
2
2
|
// http://underscorejs.org
|
3
|
-
// (c) 2009-
|
3
|
+
// (c) 2009-2015 Jeremy Ashkenas, DocumentCloud and Investigative Reporters & Editors
|
4
4
|
// Underscore may be freely distributed under the MIT license.
|
5
5
|
|
6
6
|
(function() {
|
@@ -8,40 +8,32 @@
|
|
8
8
|
// Baseline setup
|
9
9
|
// --------------
|
10
10
|
|
11
|
-
// Establish the root object, `window` in the browser, or `
|
11
|
+
// Establish the root object, `window` in the browser, or `exports` on the server.
|
12
12
|
var root = this;
|
13
13
|
|
14
14
|
// Save the previous value of the `_` variable.
|
15
15
|
var previousUnderscore = root._;
|
16
16
|
|
17
|
-
// Establish the object that gets returned to break out of a loop iteration.
|
18
|
-
var breaker = {};
|
19
|
-
|
20
17
|
// Save bytes in the minified (but not gzipped) version:
|
21
18
|
var ArrayProto = Array.prototype, ObjProto = Object.prototype, FuncProto = Function.prototype;
|
22
19
|
|
23
20
|
// Create quick reference variables for speed access to core prototypes.
|
24
|
-
var
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
21
|
+
var
|
22
|
+
push = ArrayProto.push,
|
23
|
+
slice = ArrayProto.slice,
|
24
|
+
toString = ObjProto.toString,
|
25
|
+
hasOwnProperty = ObjProto.hasOwnProperty;
|
29
26
|
|
30
27
|
// All **ECMAScript 5** native function implementations that we hope to use
|
31
28
|
// are declared here.
|
32
29
|
var
|
33
|
-
nativeForEach = ArrayProto.forEach,
|
34
|
-
nativeMap = ArrayProto.map,
|
35
|
-
nativeReduce = ArrayProto.reduce,
|
36
|
-
nativeReduceRight = ArrayProto.reduceRight,
|
37
|
-
nativeFilter = ArrayProto.filter,
|
38
|
-
nativeEvery = ArrayProto.every,
|
39
|
-
nativeSome = ArrayProto.some,
|
40
|
-
nativeIndexOf = ArrayProto.indexOf,
|
41
|
-
nativeLastIndexOf = ArrayProto.lastIndexOf,
|
42
30
|
nativeIsArray = Array.isArray,
|
43
31
|
nativeKeys = Object.keys,
|
44
|
-
nativeBind = FuncProto.bind
|
32
|
+
nativeBind = FuncProto.bind,
|
33
|
+
nativeCreate = Object.create;
|
34
|
+
|
35
|
+
// Naked function reference for surrogate-prototype-swapping.
|
36
|
+
var Ctor = function(){};
|
45
37
|
|
46
38
|
// Create a safe reference to the Underscore object for use below.
|
47
39
|
var _ = function(obj) {
|
@@ -52,8 +44,7 @@
|
|
52
44
|
|
53
45
|
// Export the Underscore object for **Node.js**, with
|
54
46
|
// backwards-compatibility for the old `require()` API. If we're in
|
55
|
-
// the browser, add `_` as a global object
|
56
|
-
// for Closure Compiler "advanced" mode.
|
47
|
+
// the browser, add `_` as a global object.
|
57
48
|
if (typeof exports !== 'undefined') {
|
58
49
|
if (typeof module !== 'undefined' && module.exports) {
|
59
50
|
exports = module.exports = _;
|
@@ -64,244 +55,329 @@
|
|
64
55
|
}
|
65
56
|
|
66
57
|
// Current version.
|
67
|
-
_.VERSION = '1.
|
58
|
+
_.VERSION = '1.8.3';
|
59
|
+
|
60
|
+
// Internal function that returns an efficient (for current engines) version
|
61
|
+
// of the passed-in callback, to be repeatedly applied in other Underscore
|
62
|
+
// functions.
|
63
|
+
var optimizeCb = function(func, context, argCount) {
|
64
|
+
if (context === void 0) return func;
|
65
|
+
switch (argCount == null ? 3 : argCount) {
|
66
|
+
case 1: return function(value) {
|
67
|
+
return func.call(context, value);
|
68
|
+
};
|
69
|
+
case 2: return function(value, other) {
|
70
|
+
return func.call(context, value, other);
|
71
|
+
};
|
72
|
+
case 3: return function(value, index, collection) {
|
73
|
+
return func.call(context, value, index, collection);
|
74
|
+
};
|
75
|
+
case 4: return function(accumulator, value, index, collection) {
|
76
|
+
return func.call(context, accumulator, value, index, collection);
|
77
|
+
};
|
78
|
+
}
|
79
|
+
return function() {
|
80
|
+
return func.apply(context, arguments);
|
81
|
+
};
|
82
|
+
};
|
83
|
+
|
84
|
+
// A mostly-internal function to generate callbacks that can be applied
|
85
|
+
// to each element in a collection, returning the desired result — either
|
86
|
+
// identity, an arbitrary callback, a property matcher, or a property accessor.
|
87
|
+
var cb = function(value, context, argCount) {
|
88
|
+
if (value == null) return _.identity;
|
89
|
+
if (_.isFunction(value)) return optimizeCb(value, context, argCount);
|
90
|
+
if (_.isObject(value)) return _.matcher(value);
|
91
|
+
return _.property(value);
|
92
|
+
};
|
93
|
+
_.iteratee = function(value, context) {
|
94
|
+
return cb(value, context, Infinity);
|
95
|
+
};
|
96
|
+
|
97
|
+
// An internal function for creating assigner functions.
|
98
|
+
var createAssigner = function(keysFunc, undefinedOnly) {
|
99
|
+
return function(obj) {
|
100
|
+
var length = arguments.length;
|
101
|
+
if (length < 2 || obj == null) return obj;
|
102
|
+
for (var index = 1; index < length; index++) {
|
103
|
+
var source = arguments[index],
|
104
|
+
keys = keysFunc(source),
|
105
|
+
l = keys.length;
|
106
|
+
for (var i = 0; i < l; i++) {
|
107
|
+
var key = keys[i];
|
108
|
+
if (!undefinedOnly || obj[key] === void 0) obj[key] = source[key];
|
109
|
+
}
|
110
|
+
}
|
111
|
+
return obj;
|
112
|
+
};
|
113
|
+
};
|
114
|
+
|
115
|
+
// An internal function for creating a new object that inherits from another.
|
116
|
+
var baseCreate = function(prototype) {
|
117
|
+
if (!_.isObject(prototype)) return {};
|
118
|
+
if (nativeCreate) return nativeCreate(prototype);
|
119
|
+
Ctor.prototype = prototype;
|
120
|
+
var result = new Ctor;
|
121
|
+
Ctor.prototype = null;
|
122
|
+
return result;
|
123
|
+
};
|
124
|
+
|
125
|
+
var property = function(key) {
|
126
|
+
return function(obj) {
|
127
|
+
return obj == null ? void 0 : obj[key];
|
128
|
+
};
|
129
|
+
};
|
130
|
+
|
131
|
+
// Helper for collection methods to determine whether a collection
|
132
|
+
// should be iterated as an array or as an object
|
133
|
+
// Related: http://people.mozilla.org/~jorendorff/es6-draft.html#sec-tolength
|
134
|
+
// Avoids a very nasty iOS 8 JIT bug on ARM-64. #2094
|
135
|
+
var MAX_ARRAY_INDEX = Math.pow(2, 53) - 1;
|
136
|
+
var getLength = property('length');
|
137
|
+
var isArrayLike = function(collection) {
|
138
|
+
var length = getLength(collection);
|
139
|
+
return typeof length == 'number' && length >= 0 && length <= MAX_ARRAY_INDEX;
|
140
|
+
};
|
68
141
|
|
69
142
|
// Collection Functions
|
70
143
|
// --------------------
|
71
144
|
|
72
145
|
// The cornerstone, an `each` implementation, aka `forEach`.
|
73
|
-
// Handles objects
|
74
|
-
//
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
if (iterator.call(context, obj[i], i, obj) === breaker) return;
|
146
|
+
// Handles raw objects in addition to array-likes. Treats all
|
147
|
+
// sparse array-likes as if they were dense.
|
148
|
+
_.each = _.forEach = function(obj, iteratee, context) {
|
149
|
+
iteratee = optimizeCb(iteratee, context);
|
150
|
+
var i, length;
|
151
|
+
if (isArrayLike(obj)) {
|
152
|
+
for (i = 0, length = obj.length; i < length; i++) {
|
153
|
+
iteratee(obj[i], i, obj);
|
82
154
|
}
|
83
155
|
} else {
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
}
|
156
|
+
var keys = _.keys(obj);
|
157
|
+
for (i = 0, length = keys.length; i < length; i++) {
|
158
|
+
iteratee(obj[keys[i]], keys[i], obj);
|
88
159
|
}
|
89
160
|
}
|
161
|
+
return obj;
|
90
162
|
};
|
91
163
|
|
92
|
-
// Return the results of applying the
|
93
|
-
|
94
|
-
|
95
|
-
var
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
164
|
+
// Return the results of applying the iteratee to each element.
|
165
|
+
_.map = _.collect = function(obj, iteratee, context) {
|
166
|
+
iteratee = cb(iteratee, context);
|
167
|
+
var keys = !isArrayLike(obj) && _.keys(obj),
|
168
|
+
length = (keys || obj).length,
|
169
|
+
results = Array(length);
|
170
|
+
for (var index = 0; index < length; index++) {
|
171
|
+
var currentKey = keys ? keys[index] : index;
|
172
|
+
results[index] = iteratee(obj[currentKey], currentKey, obj);
|
173
|
+
}
|
101
174
|
return results;
|
102
175
|
};
|
103
176
|
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
return
|
177
|
+
// Create a reducing function iterating left or right.
|
178
|
+
function createReduce(dir) {
|
179
|
+
// Optimized iterator function as using arguments.length
|
180
|
+
// in the main function will deoptimize the, see #1991.
|
181
|
+
function iterator(obj, iteratee, memo, keys, index, length) {
|
182
|
+
for (; index >= 0 && index < length; index += dir) {
|
183
|
+
var currentKey = keys ? keys[index] : index;
|
184
|
+
memo = iteratee(memo, obj[currentKey], currentKey, obj);
|
185
|
+
}
|
186
|
+
return memo;
|
114
187
|
}
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
188
|
+
|
189
|
+
return function(obj, iteratee, memo, context) {
|
190
|
+
iteratee = optimizeCb(iteratee, context, 4);
|
191
|
+
var keys = !isArrayLike(obj) && _.keys(obj),
|
192
|
+
length = (keys || obj).length,
|
193
|
+
index = dir > 0 ? 0 : length - 1;
|
194
|
+
// Determine the initial value if none is provided.
|
195
|
+
if (arguments.length < 3) {
|
196
|
+
memo = obj[keys ? keys[index] : index];
|
197
|
+
index += dir;
|
121
198
|
}
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
199
|
+
return iterator(obj, iteratee, memo, keys, index, length);
|
200
|
+
};
|
201
|
+
}
|
202
|
+
|
203
|
+
// **Reduce** builds up a single result from a list of values, aka `inject`,
|
204
|
+
// or `foldl`.
|
205
|
+
_.reduce = _.foldl = _.inject = createReduce(1);
|
126
206
|
|
127
207
|
// The right-associative version of reduce, also known as `foldr`.
|
128
|
-
|
129
|
-
_.reduceRight = _.foldr = function(obj, iterator, memo, context) {
|
130
|
-
var initial = arguments.length > 2;
|
131
|
-
if (obj == null) obj = [];
|
132
|
-
if (nativeReduceRight && obj.reduceRight === nativeReduceRight) {
|
133
|
-
if (context) iterator = _.bind(iterator, context);
|
134
|
-
return initial ? obj.reduceRight(iterator, memo) : obj.reduceRight(iterator);
|
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;
|
152
|
-
};
|
208
|
+
_.reduceRight = _.foldr = createReduce(-1);
|
153
209
|
|
154
210
|
// Return the first value which passes a truth test. Aliased as `detect`.
|
155
|
-
_.find = _.detect = function(obj,
|
156
|
-
var
|
157
|
-
|
158
|
-
|
159
|
-
|
160
|
-
|
161
|
-
|
162
|
-
|
163
|
-
return result;
|
211
|
+
_.find = _.detect = function(obj, predicate, context) {
|
212
|
+
var key;
|
213
|
+
if (isArrayLike(obj)) {
|
214
|
+
key = _.findIndex(obj, predicate, context);
|
215
|
+
} else {
|
216
|
+
key = _.findKey(obj, predicate, context);
|
217
|
+
}
|
218
|
+
if (key !== void 0 && key !== -1) return obj[key];
|
164
219
|
};
|
165
220
|
|
166
221
|
// Return all the elements that pass a truth test.
|
167
|
-
// Delegates to **ECMAScript 5**'s native `filter` if available.
|
168
222
|
// Aliased as `select`.
|
169
|
-
_.filter = _.select = function(obj,
|
223
|
+
_.filter = _.select = function(obj, predicate, context) {
|
170
224
|
var results = [];
|
171
|
-
|
172
|
-
|
173
|
-
|
174
|
-
if (iterator.call(context, value, index, list)) results[results.length] = value;
|
225
|
+
predicate = cb(predicate, context);
|
226
|
+
_.each(obj, function(value, index, list) {
|
227
|
+
if (predicate(value, index, list)) results.push(value);
|
175
228
|
});
|
176
229
|
return results;
|
177
230
|
};
|
178
231
|
|
179
232
|
// Return all the elements for which a truth test fails.
|
180
|
-
_.reject = function(obj,
|
181
|
-
return _.filter(obj,
|
182
|
-
return !iterator.call(context, value, index, list);
|
183
|
-
}, context);
|
233
|
+
_.reject = function(obj, predicate, context) {
|
234
|
+
return _.filter(obj, _.negate(cb(predicate)), context);
|
184
235
|
};
|
185
236
|
|
186
237
|
// Determine whether all of the elements match a truth test.
|
187
|
-
// Delegates to **ECMAScript 5**'s native `every` if available.
|
188
238
|
// Aliased as `all`.
|
189
|
-
_.every = _.all = function(obj,
|
190
|
-
|
191
|
-
var
|
192
|
-
|
193
|
-
|
194
|
-
|
195
|
-
if (!(
|
196
|
-
}
|
197
|
-
return
|
239
|
+
_.every = _.all = function(obj, predicate, context) {
|
240
|
+
predicate = cb(predicate, context);
|
241
|
+
var keys = !isArrayLike(obj) && _.keys(obj),
|
242
|
+
length = (keys || obj).length;
|
243
|
+
for (var index = 0; index < length; index++) {
|
244
|
+
var currentKey = keys ? keys[index] : index;
|
245
|
+
if (!predicate(obj[currentKey], currentKey, obj)) return false;
|
246
|
+
}
|
247
|
+
return true;
|
198
248
|
};
|
199
249
|
|
200
250
|
// Determine if at least one element in the object matches a truth test.
|
201
|
-
// Delegates to **ECMAScript 5**'s native `some` if available.
|
202
251
|
// Aliased as `any`.
|
203
|
-
|
204
|
-
|
205
|
-
var
|
206
|
-
|
207
|
-
|
208
|
-
|
209
|
-
if (
|
210
|
-
}
|
211
|
-
return
|
252
|
+
_.some = _.any = function(obj, predicate, context) {
|
253
|
+
predicate = cb(predicate, context);
|
254
|
+
var keys = !isArrayLike(obj) && _.keys(obj),
|
255
|
+
length = (keys || obj).length;
|
256
|
+
for (var index = 0; index < length; index++) {
|
257
|
+
var currentKey = keys ? keys[index] : index;
|
258
|
+
if (predicate(obj[currentKey], currentKey, obj)) return true;
|
259
|
+
}
|
260
|
+
return false;
|
212
261
|
};
|
213
262
|
|
214
|
-
// Determine if the array or object contains a given
|
215
|
-
// Aliased as `include`.
|
216
|
-
_.contains = _.include = function(obj,
|
217
|
-
if (obj
|
218
|
-
if (
|
219
|
-
return
|
220
|
-
return value === target;
|
221
|
-
});
|
263
|
+
// Determine if the array or object contains a given item (using `===`).
|
264
|
+
// Aliased as `includes` and `include`.
|
265
|
+
_.contains = _.includes = _.include = function(obj, item, fromIndex, guard) {
|
266
|
+
if (!isArrayLike(obj)) obj = _.values(obj);
|
267
|
+
if (typeof fromIndex != 'number' || guard) fromIndex = 0;
|
268
|
+
return _.indexOf(obj, item, fromIndex) >= 0;
|
222
269
|
};
|
223
270
|
|
224
271
|
// Invoke a method (with arguments) on every item in a collection.
|
225
272
|
_.invoke = function(obj, method) {
|
226
273
|
var args = slice.call(arguments, 2);
|
274
|
+
var isFunc = _.isFunction(method);
|
227
275
|
return _.map(obj, function(value) {
|
228
|
-
|
276
|
+
var func = isFunc ? method : value[method];
|
277
|
+
return func == null ? func : func.apply(value, args);
|
229
278
|
});
|
230
279
|
};
|
231
280
|
|
232
281
|
// Convenience version of a common use case of `map`: fetching a property.
|
233
282
|
_.pluck = function(obj, key) {
|
234
|
-
return _.map(obj,
|
283
|
+
return _.map(obj, _.property(key));
|
235
284
|
};
|
236
285
|
|
237
286
|
// Convenience version of a common use case of `filter`: selecting only objects
|
238
|
-
//
|
287
|
+
// containing specific `key:value` pairs.
|
239
288
|
_.where = function(obj, attrs) {
|
240
|
-
|
241
|
-
return _.filter(obj, function(value) {
|
242
|
-
for (var key in attrs) {
|
243
|
-
if (attrs[key] !== value[key]) return false;
|
244
|
-
}
|
245
|
-
return true;
|
246
|
-
});
|
289
|
+
return _.filter(obj, _.matcher(attrs));
|
247
290
|
};
|
248
291
|
|
249
|
-
//
|
250
|
-
//
|
251
|
-
|
252
|
-
|
253
|
-
|
254
|
-
|
292
|
+
// Convenience version of a common use case of `find`: getting the first object
|
293
|
+
// containing specific `key:value` pairs.
|
294
|
+
_.findWhere = function(obj, attrs) {
|
295
|
+
return _.find(obj, _.matcher(attrs));
|
296
|
+
};
|
297
|
+
|
298
|
+
// Return the maximum element (or element-based computation).
|
299
|
+
_.max = function(obj, iteratee, context) {
|
300
|
+
var result = -Infinity, lastComputed = -Infinity,
|
301
|
+
value, computed;
|
302
|
+
if (iteratee == null && obj != null) {
|
303
|
+
obj = isArrayLike(obj) ? obj : _.values(obj);
|
304
|
+
for (var i = 0, length = obj.length; i < length; i++) {
|
305
|
+
value = obj[i];
|
306
|
+
if (value > result) {
|
307
|
+
result = value;
|
308
|
+
}
|
309
|
+
}
|
310
|
+
} else {
|
311
|
+
iteratee = cb(iteratee, context);
|
312
|
+
_.each(obj, function(value, index, list) {
|
313
|
+
computed = iteratee(value, index, list);
|
314
|
+
if (computed > lastComputed || computed === -Infinity && result === -Infinity) {
|
315
|
+
result = value;
|
316
|
+
lastComputed = computed;
|
317
|
+
}
|
318
|
+
});
|
255
319
|
}
|
256
|
-
|
257
|
-
var result = {computed : -Infinity, value: -Infinity};
|
258
|
-
each(obj, function(value, index, list) {
|
259
|
-
var computed = iterator ? iterator.call(context, value, index, list) : value;
|
260
|
-
computed >= result.computed && (result = {value : value, computed : computed});
|
261
|
-
});
|
262
|
-
return result.value;
|
320
|
+
return result;
|
263
321
|
};
|
264
322
|
|
265
323
|
// Return the minimum element (or element-based computation).
|
266
|
-
_.min = function(obj,
|
267
|
-
|
268
|
-
|
324
|
+
_.min = function(obj, iteratee, context) {
|
325
|
+
var result = Infinity, lastComputed = Infinity,
|
326
|
+
value, computed;
|
327
|
+
if (iteratee == null && obj != null) {
|
328
|
+
obj = isArrayLike(obj) ? obj : _.values(obj);
|
329
|
+
for (var i = 0, length = obj.length; i < length; i++) {
|
330
|
+
value = obj[i];
|
331
|
+
if (value < result) {
|
332
|
+
result = value;
|
333
|
+
}
|
334
|
+
}
|
335
|
+
} else {
|
336
|
+
iteratee = cb(iteratee, context);
|
337
|
+
_.each(obj, function(value, index, list) {
|
338
|
+
computed = iteratee(value, index, list);
|
339
|
+
if (computed < lastComputed || computed === Infinity && result === Infinity) {
|
340
|
+
result = value;
|
341
|
+
lastComputed = computed;
|
342
|
+
}
|
343
|
+
});
|
269
344
|
}
|
270
|
-
|
271
|
-
var result = {computed : Infinity, value: Infinity};
|
272
|
-
each(obj, function(value, index, list) {
|
273
|
-
var computed = iterator ? iterator.call(context, value, index, list) : value;
|
274
|
-
computed < result.computed && (result = {value : value, computed : computed});
|
275
|
-
});
|
276
|
-
return result.value;
|
345
|
+
return result;
|
277
346
|
};
|
278
347
|
|
279
|
-
// Shuffle
|
348
|
+
// Shuffle a collection, using the modern version of the
|
349
|
+
// [Fisher-Yates shuffle](http://en.wikipedia.org/wiki/Fisher–Yates_shuffle).
|
280
350
|
_.shuffle = function(obj) {
|
281
|
-
var
|
282
|
-
var
|
283
|
-
var shuffled =
|
284
|
-
|
285
|
-
rand = _.random(index
|
286
|
-
shuffled[index
|
287
|
-
shuffled[rand] =
|
288
|
-
}
|
351
|
+
var set = isArrayLike(obj) ? obj : _.values(obj);
|
352
|
+
var length = set.length;
|
353
|
+
var shuffled = Array(length);
|
354
|
+
for (var index = 0, rand; index < length; index++) {
|
355
|
+
rand = _.random(0, index);
|
356
|
+
if (rand !== index) shuffled[index] = shuffled[rand];
|
357
|
+
shuffled[rand] = set[index];
|
358
|
+
}
|
289
359
|
return shuffled;
|
290
360
|
};
|
291
361
|
|
292
|
-
//
|
293
|
-
|
294
|
-
|
362
|
+
// Sample **n** random values from a collection.
|
363
|
+
// If **n** is not specified, returns a single random element.
|
364
|
+
// The internal `guard` argument allows it to work with `map`.
|
365
|
+
_.sample = function(obj, n, guard) {
|
366
|
+
if (n == null || guard) {
|
367
|
+
if (!isArrayLike(obj)) obj = _.values(obj);
|
368
|
+
return obj[_.random(obj.length - 1)];
|
369
|
+
}
|
370
|
+
return _.shuffle(obj).slice(0, Math.max(0, n));
|
295
371
|
};
|
296
372
|
|
297
|
-
// Sort the object's values by a criterion produced by an
|
298
|
-
_.sortBy = function(obj,
|
299
|
-
|
373
|
+
// Sort the object's values by a criterion produced by an iteratee.
|
374
|
+
_.sortBy = function(obj, iteratee, context) {
|
375
|
+
iteratee = cb(iteratee, context);
|
300
376
|
return _.pluck(_.map(obj, function(value, index, list) {
|
301
377
|
return {
|
302
|
-
value
|
303
|
-
index
|
304
|
-
criteria
|
378
|
+
value: value,
|
379
|
+
index: index,
|
380
|
+
criteria: iteratee(value, index, list)
|
305
381
|
};
|
306
382
|
}).sort(function(left, right) {
|
307
383
|
var a = left.criteria;
|
@@ -310,64 +386,65 @@
|
|
310
386
|
if (a > b || a === void 0) return 1;
|
311
387
|
if (a < b || b === void 0) return -1;
|
312
388
|
}
|
313
|
-
return left.index
|
389
|
+
return left.index - right.index;
|
314
390
|
}), 'value');
|
315
391
|
};
|
316
392
|
|
317
393
|
// An internal function used for aggregate "group by" operations.
|
318
|
-
var group = function(
|
319
|
-
|
320
|
-
|
321
|
-
|
322
|
-
|
323
|
-
|
324
|
-
|
325
|
-
|
394
|
+
var group = function(behavior) {
|
395
|
+
return function(obj, iteratee, context) {
|
396
|
+
var result = {};
|
397
|
+
iteratee = cb(iteratee, context);
|
398
|
+
_.each(obj, function(value, index) {
|
399
|
+
var key = iteratee(value, index, obj);
|
400
|
+
behavior(result, value, key);
|
401
|
+
});
|
402
|
+
return result;
|
403
|
+
};
|
326
404
|
};
|
327
405
|
|
328
406
|
// Groups the object's values by a criterion. Pass either a string attribute
|
329
407
|
// to group by, or a function that returns the criterion.
|
330
|
-
_.groupBy = function(
|
331
|
-
|
332
|
-
|
333
|
-
|
334
|
-
|
408
|
+
_.groupBy = group(function(result, value, key) {
|
409
|
+
if (_.has(result, key)) result[key].push(value); else result[key] = [value];
|
410
|
+
});
|
411
|
+
|
412
|
+
// Indexes the object's values by a criterion, similar to `groupBy`, but for
|
413
|
+
// when you know that your index values will be unique.
|
414
|
+
_.indexBy = group(function(result, value, key) {
|
415
|
+
result[key] = value;
|
416
|
+
});
|
335
417
|
|
336
418
|
// Counts instances of an object that group by a certain criterion. Pass
|
337
419
|
// either a string attribute to count by, or a function that returns the
|
338
420
|
// criterion.
|
339
|
-
_.countBy = function(
|
340
|
-
|
341
|
-
|
342
|
-
result[key]++;
|
343
|
-
});
|
344
|
-
};
|
345
|
-
|
346
|
-
// Use a comparator function to figure out the smallest index at which
|
347
|
-
// an object should be inserted so as to maintain order. Uses binary search.
|
348
|
-
_.sortedIndex = function(array, obj, iterator, context) {
|
349
|
-
iterator = iterator == null ? _.identity : lookupIterator(iterator);
|
350
|
-
var value = iterator.call(context, obj);
|
351
|
-
var low = 0, high = array.length;
|
352
|
-
while (low < high) {
|
353
|
-
var mid = (low + high) >>> 1;
|
354
|
-
iterator.call(context, array[mid]) < value ? low = mid + 1 : high = mid;
|
355
|
-
}
|
356
|
-
return low;
|
357
|
-
};
|
421
|
+
_.countBy = group(function(result, value, key) {
|
422
|
+
if (_.has(result, key)) result[key]++; else result[key] = 1;
|
423
|
+
});
|
358
424
|
|
359
|
-
// Safely
|
425
|
+
// Safely create a real, live array from anything iterable.
|
360
426
|
_.toArray = function(obj) {
|
361
427
|
if (!obj) return [];
|
362
428
|
if (_.isArray(obj)) return slice.call(obj);
|
363
|
-
if (obj
|
429
|
+
if (isArrayLike(obj)) return _.map(obj, _.identity);
|
364
430
|
return _.values(obj);
|
365
431
|
};
|
366
432
|
|
367
433
|
// Return the number of elements in an object.
|
368
434
|
_.size = function(obj) {
|
369
435
|
if (obj == null) return 0;
|
370
|
-
return (obj
|
436
|
+
return isArrayLike(obj) ? obj.length : _.keys(obj).length;
|
437
|
+
};
|
438
|
+
|
439
|
+
// Split a collection into two arrays: one whose elements all satisfy the given
|
440
|
+
// predicate, and one whose elements all do not satisfy the predicate.
|
441
|
+
_.partition = function(obj, predicate, context) {
|
442
|
+
predicate = cb(predicate, context);
|
443
|
+
var pass = [], fail = [];
|
444
|
+
_.each(obj, function(value, key, obj) {
|
445
|
+
(predicate(value, key, obj) ? pass : fail).push(value);
|
446
|
+
});
|
447
|
+
return [pass, fail];
|
371
448
|
};
|
372
449
|
|
373
450
|
// Array Functions
|
@@ -378,34 +455,30 @@
|
|
378
455
|
// allows it to work with `_.map`.
|
379
456
|
_.first = _.head = _.take = function(array, n, guard) {
|
380
457
|
if (array == null) return void 0;
|
381
|
-
|
458
|
+
if (n == null || guard) return array[0];
|
459
|
+
return _.initial(array, array.length - n);
|
382
460
|
};
|
383
461
|
|
384
462
|
// Returns everything but the last entry of the array. Especially useful on
|
385
463
|
// the arguments object. Passing **n** will return all the values in
|
386
|
-
// the array, excluding the last N.
|
387
|
-
// `_.map`.
|
464
|
+
// the array, excluding the last N.
|
388
465
|
_.initial = function(array, n, guard) {
|
389
|
-
return slice.call(array, 0, array.length - (
|
466
|
+
return slice.call(array, 0, Math.max(0, array.length - (n == null || guard ? 1 : n)));
|
390
467
|
};
|
391
468
|
|
392
469
|
// Get the last element of an array. Passing **n** will return the last N
|
393
|
-
// values in the array.
|
470
|
+
// values in the array.
|
394
471
|
_.last = function(array, n, guard) {
|
395
472
|
if (array == null) return void 0;
|
396
|
-
if (
|
397
|
-
|
398
|
-
} else {
|
399
|
-
return array[array.length - 1];
|
400
|
-
}
|
473
|
+
if (n == null || guard) return array[array.length - 1];
|
474
|
+
return _.rest(array, Math.max(0, array.length - n));
|
401
475
|
};
|
402
476
|
|
403
477
|
// Returns everything but the first entry of the array. Aliased as `tail` and `drop`.
|
404
478
|
// Especially useful on the arguments object. Passing an **n** will return
|
405
|
-
// the rest N values in the array.
|
406
|
-
// check allows it to work with `_.map`.
|
479
|
+
// the rest N values in the array.
|
407
480
|
_.rest = _.tail = _.drop = function(array, n, guard) {
|
408
|
-
return slice.call(array,
|
481
|
+
return slice.call(array, n == null || guard ? 1 : n);
|
409
482
|
};
|
410
483
|
|
411
484
|
// Trim out all falsy values from an array.
|
@@ -414,20 +487,28 @@
|
|
414
487
|
};
|
415
488
|
|
416
489
|
// Internal implementation of a recursive `flatten` function.
|
417
|
-
var flatten = function(input, shallow,
|
418
|
-
|
419
|
-
|
420
|
-
|
421
|
-
|
422
|
-
|
490
|
+
var flatten = function(input, shallow, strict, startIndex) {
|
491
|
+
var output = [], idx = 0;
|
492
|
+
for (var i = startIndex || 0, length = getLength(input); i < length; i++) {
|
493
|
+
var value = input[i];
|
494
|
+
if (isArrayLike(value) && (_.isArray(value) || _.isArguments(value))) {
|
495
|
+
//flatten current level of array or arguments object
|
496
|
+
if (!shallow) value = flatten(value, shallow, strict);
|
497
|
+
var j = 0, len = value.length;
|
498
|
+
output.length += len;
|
499
|
+
while (j < len) {
|
500
|
+
output[idx++] = value[j++];
|
501
|
+
}
|
502
|
+
} else if (!strict) {
|
503
|
+
output[idx++] = value;
|
423
504
|
}
|
424
|
-
}
|
505
|
+
}
|
425
506
|
return output;
|
426
507
|
};
|
427
508
|
|
428
|
-
//
|
509
|
+
// Flatten out an array, either recursively (by default), or just one level.
|
429
510
|
_.flatten = function(array, shallow) {
|
430
|
-
return flatten(array, shallow,
|
511
|
+
return flatten(array, shallow, false);
|
431
512
|
};
|
432
513
|
|
433
514
|
// Return a version of the array that does not contain the specified value(s).
|
@@ -438,67 +519,88 @@
|
|
438
519
|
// Produce a duplicate-free version of the array. If the array has already
|
439
520
|
// been sorted, you have the option of using a faster algorithm.
|
440
521
|
// Aliased as `unique`.
|
441
|
-
_.uniq = _.unique = function(array, isSorted,
|
442
|
-
if (_.
|
443
|
-
context =
|
444
|
-
|
522
|
+
_.uniq = _.unique = function(array, isSorted, iteratee, context) {
|
523
|
+
if (!_.isBoolean(isSorted)) {
|
524
|
+
context = iteratee;
|
525
|
+
iteratee = isSorted;
|
445
526
|
isSorted = false;
|
446
527
|
}
|
447
|
-
|
448
|
-
var
|
528
|
+
if (iteratee != null) iteratee = cb(iteratee, context);
|
529
|
+
var result = [];
|
449
530
|
var seen = [];
|
450
|
-
|
451
|
-
|
452
|
-
|
453
|
-
|
531
|
+
for (var i = 0, length = getLength(array); i < length; i++) {
|
532
|
+
var value = array[i],
|
533
|
+
computed = iteratee ? iteratee(value, i, array) : value;
|
534
|
+
if (isSorted) {
|
535
|
+
if (!i || seen !== computed) result.push(value);
|
536
|
+
seen = computed;
|
537
|
+
} else if (iteratee) {
|
538
|
+
if (!_.contains(seen, computed)) {
|
539
|
+
seen.push(computed);
|
540
|
+
result.push(value);
|
541
|
+
}
|
542
|
+
} else if (!_.contains(result, value)) {
|
543
|
+
result.push(value);
|
454
544
|
}
|
455
|
-
}
|
456
|
-
return
|
545
|
+
}
|
546
|
+
return result;
|
457
547
|
};
|
458
548
|
|
459
549
|
// Produce an array that contains the union: each distinct element from all of
|
460
550
|
// the passed-in arrays.
|
461
551
|
_.union = function() {
|
462
|
-
return _.uniq(
|
552
|
+
return _.uniq(flatten(arguments, true, true));
|
463
553
|
};
|
464
554
|
|
465
555
|
// Produce an array that contains every item shared between all the
|
466
556
|
// passed-in arrays.
|
467
557
|
_.intersection = function(array) {
|
468
|
-
var
|
469
|
-
|
470
|
-
|
471
|
-
|
472
|
-
|
473
|
-
|
558
|
+
var result = [];
|
559
|
+
var argsLength = arguments.length;
|
560
|
+
for (var i = 0, length = getLength(array); i < length; i++) {
|
561
|
+
var item = array[i];
|
562
|
+
if (_.contains(result, item)) continue;
|
563
|
+
for (var j = 1; j < argsLength; j++) {
|
564
|
+
if (!_.contains(arguments[j], item)) break;
|
565
|
+
}
|
566
|
+
if (j === argsLength) result.push(item);
|
567
|
+
}
|
568
|
+
return result;
|
474
569
|
};
|
475
570
|
|
476
571
|
// Take the difference between one array and a number of other arrays.
|
477
572
|
// Only the elements present in just the first array will remain.
|
478
573
|
_.difference = function(array) {
|
479
|
-
var rest =
|
480
|
-
return _.filter(array, function(value){
|
574
|
+
var rest = flatten(arguments, true, true, 1);
|
575
|
+
return _.filter(array, function(value){
|
576
|
+
return !_.contains(rest, value);
|
577
|
+
});
|
481
578
|
};
|
482
579
|
|
483
580
|
// Zip together multiple lists into a single array -- elements that share
|
484
581
|
// an index go together.
|
485
582
|
_.zip = function() {
|
486
|
-
|
487
|
-
|
488
|
-
|
489
|
-
|
490
|
-
|
583
|
+
return _.unzip(arguments);
|
584
|
+
};
|
585
|
+
|
586
|
+
// Complement of _.zip. Unzip accepts an array of arrays and groups
|
587
|
+
// each array's elements on shared indices
|
588
|
+
_.unzip = function(array) {
|
589
|
+
var length = array && _.max(array, getLength).length || 0;
|
590
|
+
var result = Array(length);
|
591
|
+
|
592
|
+
for (var index = 0; index < length; index++) {
|
593
|
+
result[index] = _.pluck(array, index);
|
491
594
|
}
|
492
|
-
return
|
595
|
+
return result;
|
493
596
|
};
|
494
597
|
|
495
598
|
// Converts lists into objects. Pass either a single array of `[key, value]`
|
496
599
|
// pairs, or two parallel arrays of the same length -- one of keys, and one of
|
497
600
|
// the corresponding values.
|
498
601
|
_.object = function(list, values) {
|
499
|
-
if (list == null) return {};
|
500
602
|
var result = {};
|
501
|
-
for (var i = 0,
|
603
|
+
for (var i = 0, length = getLength(list); i < length; i++) {
|
502
604
|
if (values) {
|
503
605
|
result[list[i]] = values[i];
|
504
606
|
} else {
|
@@ -508,57 +610,83 @@
|
|
508
610
|
return result;
|
509
611
|
};
|
510
612
|
|
511
|
-
//
|
512
|
-
|
513
|
-
|
514
|
-
|
515
|
-
|
516
|
-
|
517
|
-
|
518
|
-
|
519
|
-
var i = 0, l = array.length;
|
520
|
-
if (isSorted) {
|
521
|
-
if (typeof isSorted == 'number') {
|
522
|
-
i = (isSorted < 0 ? Math.max(0, l + isSorted) : isSorted);
|
523
|
-
} else {
|
524
|
-
i = _.sortedIndex(array, item);
|
525
|
-
return array[i] === item ? i : -1;
|
613
|
+
// Generator function to create the findIndex and findLastIndex functions
|
614
|
+
function createPredicateIndexFinder(dir) {
|
615
|
+
return function(array, predicate, context) {
|
616
|
+
predicate = cb(predicate, context);
|
617
|
+
var length = getLength(array);
|
618
|
+
var index = dir > 0 ? 0 : length - 1;
|
619
|
+
for (; index >= 0 && index < length; index += dir) {
|
620
|
+
if (predicate(array[index], index, array)) return index;
|
526
621
|
}
|
527
|
-
|
528
|
-
|
529
|
-
|
530
|
-
return -1;
|
531
|
-
};
|
622
|
+
return -1;
|
623
|
+
};
|
624
|
+
}
|
532
625
|
|
533
|
-
//
|
534
|
-
_.
|
535
|
-
|
536
|
-
|
537
|
-
|
538
|
-
|
626
|
+
// Returns the first index on an array-like that passes a predicate test
|
627
|
+
_.findIndex = createPredicateIndexFinder(1);
|
628
|
+
_.findLastIndex = createPredicateIndexFinder(-1);
|
629
|
+
|
630
|
+
// Use a comparator function to figure out the smallest index at which
|
631
|
+
// an object should be inserted so as to maintain order. Uses binary search.
|
632
|
+
_.sortedIndex = function(array, obj, iteratee, context) {
|
633
|
+
iteratee = cb(iteratee, context, 1);
|
634
|
+
var value = iteratee(obj);
|
635
|
+
var low = 0, high = getLength(array);
|
636
|
+
while (low < high) {
|
637
|
+
var mid = Math.floor((low + high) / 2);
|
638
|
+
if (iteratee(array[mid]) < value) low = mid + 1; else high = mid;
|
539
639
|
}
|
540
|
-
|
541
|
-
while (i--) if (array[i] === item) return i;
|
542
|
-
return -1;
|
640
|
+
return low;
|
543
641
|
};
|
544
642
|
|
643
|
+
// Generator function to create the indexOf and lastIndexOf functions
|
644
|
+
function createIndexFinder(dir, predicateFind, sortedIndex) {
|
645
|
+
return function(array, item, idx) {
|
646
|
+
var i = 0, length = getLength(array);
|
647
|
+
if (typeof idx == 'number') {
|
648
|
+
if (dir > 0) {
|
649
|
+
i = idx >= 0 ? idx : Math.max(idx + length, i);
|
650
|
+
} else {
|
651
|
+
length = idx >= 0 ? Math.min(idx + 1, length) : idx + length + 1;
|
652
|
+
}
|
653
|
+
} else if (sortedIndex && idx && length) {
|
654
|
+
idx = sortedIndex(array, item);
|
655
|
+
return array[idx] === item ? idx : -1;
|
656
|
+
}
|
657
|
+
if (item !== item) {
|
658
|
+
idx = predicateFind(slice.call(array, i, length), _.isNaN);
|
659
|
+
return idx >= 0 ? idx + i : -1;
|
660
|
+
}
|
661
|
+
for (idx = dir > 0 ? i : length - 1; idx >= 0 && idx < length; idx += dir) {
|
662
|
+
if (array[idx] === item) return idx;
|
663
|
+
}
|
664
|
+
return -1;
|
665
|
+
};
|
666
|
+
}
|
667
|
+
|
668
|
+
// Return the position of the first occurrence of an item in an array,
|
669
|
+
// or -1 if the item is not included in the array.
|
670
|
+
// If the array is large and already in sort order, pass `true`
|
671
|
+
// for **isSorted** to use binary search.
|
672
|
+
_.indexOf = createIndexFinder(1, _.findIndex, _.sortedIndex);
|
673
|
+
_.lastIndexOf = createIndexFinder(-1, _.findLastIndex);
|
674
|
+
|
545
675
|
// Generate an integer Array containing an arithmetic progression. A port of
|
546
676
|
// the native Python `range()` function. See
|
547
677
|
// [the Python documentation](http://docs.python.org/library/functions.html#range).
|
548
678
|
_.range = function(start, stop, step) {
|
549
|
-
if (
|
679
|
+
if (stop == null) {
|
550
680
|
stop = start || 0;
|
551
681
|
start = 0;
|
552
682
|
}
|
553
|
-
step =
|
683
|
+
step = step || 1;
|
554
684
|
|
555
|
-
var
|
556
|
-
var
|
557
|
-
var range = new Array(len);
|
685
|
+
var length = Math.max(Math.ceil((stop - start) / step), 0);
|
686
|
+
var range = Array(length);
|
558
687
|
|
559
|
-
|
560
|
-
range[idx
|
561
|
-
start += step;
|
688
|
+
for (var idx = 0; idx < length; idx++, start += step) {
|
689
|
+
range[idx] = start;
|
562
690
|
}
|
563
691
|
|
564
692
|
return range;
|
@@ -567,82 +695,115 @@
|
|
567
695
|
// Function (ahem) Functions
|
568
696
|
// ------------------
|
569
697
|
|
570
|
-
//
|
571
|
-
|
698
|
+
// Determines whether to execute a function as a constructor
|
699
|
+
// or a normal function with the provided arguments
|
700
|
+
var executeBound = function(sourceFunc, boundFunc, context, callingContext, args) {
|
701
|
+
if (!(callingContext instanceof boundFunc)) return sourceFunc.apply(context, args);
|
702
|
+
var self = baseCreate(sourceFunc.prototype);
|
703
|
+
var result = sourceFunc.apply(self, args);
|
704
|
+
if (_.isObject(result)) return result;
|
705
|
+
return self;
|
706
|
+
};
|
572
707
|
|
573
708
|
// Create a function bound to a given object (assigning `this`, and arguments,
|
574
|
-
// optionally).
|
575
|
-
//
|
576
|
-
// We check for `func.bind` first, to fail fast when `func` is undefined.
|
709
|
+
// optionally). Delegates to **ECMAScript 5**'s native `Function.bind` if
|
710
|
+
// available.
|
577
711
|
_.bind = function(func, context) {
|
578
|
-
|
579
|
-
if (func
|
580
|
-
|
581
|
-
|
582
|
-
|
583
|
-
if (!(this instanceof bound)) return func.apply(context, args.concat(slice.call(arguments)));
|
584
|
-
ctor.prototype = func.prototype;
|
585
|
-
var self = new ctor;
|
586
|
-
ctor.prototype = null;
|
587
|
-
var result = func.apply(self, args.concat(slice.call(arguments)));
|
588
|
-
if (Object(result) === result) return result;
|
589
|
-
return self;
|
712
|
+
if (nativeBind && func.bind === nativeBind) return nativeBind.apply(func, slice.call(arguments, 1));
|
713
|
+
if (!_.isFunction(func)) throw new TypeError('Bind must be called on a function');
|
714
|
+
var args = slice.call(arguments, 2);
|
715
|
+
var bound = function() {
|
716
|
+
return executeBound(func, bound, context, this, args.concat(slice.call(arguments)));
|
590
717
|
};
|
718
|
+
return bound;
|
719
|
+
};
|
720
|
+
|
721
|
+
// Partially apply a function by creating a version that has had some of its
|
722
|
+
// arguments pre-filled, without changing its dynamic `this` context. _ acts
|
723
|
+
// as a placeholder, allowing any combination of arguments to be pre-filled.
|
724
|
+
_.partial = function(func) {
|
725
|
+
var boundArgs = slice.call(arguments, 1);
|
726
|
+
var bound = function() {
|
727
|
+
var position = 0, length = boundArgs.length;
|
728
|
+
var args = Array(length);
|
729
|
+
for (var i = 0; i < length; i++) {
|
730
|
+
args[i] = boundArgs[i] === _ ? arguments[position++] : boundArgs[i];
|
731
|
+
}
|
732
|
+
while (position < arguments.length) args.push(arguments[position++]);
|
733
|
+
return executeBound(func, bound, this, this, args);
|
734
|
+
};
|
735
|
+
return bound;
|
591
736
|
};
|
592
737
|
|
593
|
-
// Bind
|
594
|
-
//
|
738
|
+
// Bind a number of an object's methods to that object. Remaining arguments
|
739
|
+
// are the method names to be bound. Useful for ensuring that all callbacks
|
740
|
+
// defined on an object belong to it.
|
595
741
|
_.bindAll = function(obj) {
|
596
|
-
var
|
597
|
-
if (
|
598
|
-
|
742
|
+
var i, length = arguments.length, key;
|
743
|
+
if (length <= 1) throw new Error('bindAll must be passed function names');
|
744
|
+
for (i = 1; i < length; i++) {
|
745
|
+
key = arguments[i];
|
746
|
+
obj[key] = _.bind(obj[key], obj);
|
747
|
+
}
|
599
748
|
return obj;
|
600
749
|
};
|
601
750
|
|
602
751
|
// Memoize an expensive function by storing its results.
|
603
752
|
_.memoize = function(func, hasher) {
|
604
|
-
var
|
605
|
-
|
606
|
-
|
607
|
-
|
608
|
-
return
|
753
|
+
var memoize = function(key) {
|
754
|
+
var cache = memoize.cache;
|
755
|
+
var address = '' + (hasher ? hasher.apply(this, arguments) : key);
|
756
|
+
if (!_.has(cache, address)) cache[address] = func.apply(this, arguments);
|
757
|
+
return cache[address];
|
609
758
|
};
|
759
|
+
memoize.cache = {};
|
760
|
+
return memoize;
|
610
761
|
};
|
611
762
|
|
612
763
|
// Delays a function for the given number of milliseconds, and then calls
|
613
764
|
// it with the arguments supplied.
|
614
765
|
_.delay = function(func, wait) {
|
615
766
|
var args = slice.call(arguments, 2);
|
616
|
-
return setTimeout(function(){
|
767
|
+
return setTimeout(function(){
|
768
|
+
return func.apply(null, args);
|
769
|
+
}, wait);
|
617
770
|
};
|
618
771
|
|
619
772
|
// Defers a function, scheduling it to run after the current call stack has
|
620
773
|
// cleared.
|
621
|
-
_.defer =
|
622
|
-
return _.delay.apply(_, [func, 1].concat(slice.call(arguments, 1)));
|
623
|
-
};
|
774
|
+
_.defer = _.partial(_.delay, _, 1);
|
624
775
|
|
625
776
|
// Returns a function, that, when invoked, will only be triggered at most once
|
626
|
-
// during a given window of time.
|
627
|
-
|
628
|
-
|
777
|
+
// during a given window of time. Normally, the throttled function will run
|
778
|
+
// as much as it can, without ever going more than once per `wait` duration;
|
779
|
+
// but if you'd like to disable the execution on the leading edge, pass
|
780
|
+
// `{leading: false}`. To disable execution on the trailing edge, ditto.
|
781
|
+
_.throttle = function(func, wait, options) {
|
782
|
+
var context, args, result;
|
783
|
+
var timeout = null;
|
629
784
|
var previous = 0;
|
785
|
+
if (!options) options = {};
|
630
786
|
var later = function() {
|
631
|
-
previous =
|
787
|
+
previous = options.leading === false ? 0 : _.now();
|
632
788
|
timeout = null;
|
633
789
|
result = func.apply(context, args);
|
790
|
+
if (!timeout) context = args = null;
|
634
791
|
};
|
635
792
|
return function() {
|
636
|
-
var now =
|
793
|
+
var now = _.now();
|
794
|
+
if (!previous && options.leading === false) previous = now;
|
637
795
|
var remaining = wait - (now - previous);
|
638
796
|
context = this;
|
639
797
|
args = arguments;
|
640
|
-
if (remaining <= 0) {
|
641
|
-
|
642
|
-
|
798
|
+
if (remaining <= 0 || remaining > wait) {
|
799
|
+
if (timeout) {
|
800
|
+
clearTimeout(timeout);
|
801
|
+
timeout = null;
|
802
|
+
}
|
643
803
|
previous = now;
|
644
804
|
result = func.apply(context, args);
|
645
|
-
|
805
|
+
if (!timeout) context = args = null;
|
806
|
+
} else if (!timeout && options.trailing !== false) {
|
646
807
|
timeout = setTimeout(later, remaining);
|
647
808
|
}
|
648
809
|
return result;
|
@@ -654,31 +815,34 @@
|
|
654
815
|
// N milliseconds. If `immediate` is passed, trigger the function on the
|
655
816
|
// leading edge, instead of the trailing.
|
656
817
|
_.debounce = function(func, wait, immediate) {
|
657
|
-
var timeout, result;
|
658
|
-
|
659
|
-
|
660
|
-
var
|
818
|
+
var timeout, args, context, timestamp, result;
|
819
|
+
|
820
|
+
var later = function() {
|
821
|
+
var last = _.now() - timestamp;
|
822
|
+
|
823
|
+
if (last < wait && last >= 0) {
|
824
|
+
timeout = setTimeout(later, wait - last);
|
825
|
+
} else {
|
661
826
|
timeout = null;
|
662
|
-
if (!immediate)
|
663
|
-
|
664
|
-
|
665
|
-
|
666
|
-
|
667
|
-
if (callNow) result = func.apply(context, args);
|
668
|
-
return result;
|
827
|
+
if (!immediate) {
|
828
|
+
result = func.apply(context, args);
|
829
|
+
if (!timeout) context = args = null;
|
830
|
+
}
|
831
|
+
}
|
669
832
|
};
|
670
|
-
};
|
671
833
|
|
672
|
-
// Returns a function that will be executed at most one time, no matter how
|
673
|
-
// often you call it. Useful for lazy initialization.
|
674
|
-
_.once = function(func) {
|
675
|
-
var ran = false, memo;
|
676
834
|
return function() {
|
677
|
-
|
678
|
-
|
679
|
-
|
680
|
-
|
681
|
-
|
835
|
+
context = this;
|
836
|
+
args = arguments;
|
837
|
+
timestamp = _.now();
|
838
|
+
var callNow = immediate && !timeout;
|
839
|
+
if (!timeout) timeout = setTimeout(later, wait);
|
840
|
+
if (callNow) {
|
841
|
+
result = func.apply(context, args);
|
842
|
+
context = args = null;
|
843
|
+
}
|
844
|
+
|
845
|
+
return result;
|
682
846
|
};
|
683
847
|
};
|
684
848
|
|
@@ -686,29 +850,31 @@
|
|
686
850
|
// allowing you to adjust arguments, run code before and after, and
|
687
851
|
// conditionally execute the original function.
|
688
852
|
_.wrap = function(func, wrapper) {
|
853
|
+
return _.partial(wrapper, func);
|
854
|
+
};
|
855
|
+
|
856
|
+
// Returns a negated version of the passed-in predicate.
|
857
|
+
_.negate = function(predicate) {
|
689
858
|
return function() {
|
690
|
-
|
691
|
-
push.apply(args, arguments);
|
692
|
-
return wrapper.apply(this, args);
|
859
|
+
return !predicate.apply(this, arguments);
|
693
860
|
};
|
694
861
|
};
|
695
862
|
|
696
863
|
// Returns a function that is the composition of a list of functions, each
|
697
864
|
// consuming the return value of the function that follows.
|
698
865
|
_.compose = function() {
|
699
|
-
var
|
866
|
+
var args = arguments;
|
867
|
+
var start = args.length - 1;
|
700
868
|
return function() {
|
701
|
-
var
|
702
|
-
|
703
|
-
|
704
|
-
|
705
|
-
return args[0];
|
869
|
+
var i = start;
|
870
|
+
var result = args[start].apply(this, arguments);
|
871
|
+
while (i--) result = args[i].call(this, result);
|
872
|
+
return result;
|
706
873
|
};
|
707
874
|
};
|
708
875
|
|
709
|
-
// Returns a function that will only be executed after
|
876
|
+
// Returns a function that will only be executed on and after the Nth call.
|
710
877
|
_.after = function(times, func) {
|
711
|
-
if (times <= 0) return func();
|
712
878
|
return function() {
|
713
879
|
if (--times < 1) {
|
714
880
|
return func.apply(this, arguments);
|
@@ -716,36 +882,113 @@
|
|
716
882
|
};
|
717
883
|
};
|
718
884
|
|
885
|
+
// Returns a function that will only be executed up to (but not including) the Nth call.
|
886
|
+
_.before = function(times, func) {
|
887
|
+
var memo;
|
888
|
+
return function() {
|
889
|
+
if (--times > 0) {
|
890
|
+
memo = func.apply(this, arguments);
|
891
|
+
}
|
892
|
+
if (times <= 1) func = null;
|
893
|
+
return memo;
|
894
|
+
};
|
895
|
+
};
|
896
|
+
|
897
|
+
// Returns a function that will be executed at most one time, no matter how
|
898
|
+
// often you call it. Useful for lazy initialization.
|
899
|
+
_.once = _.partial(_.before, 2);
|
900
|
+
|
719
901
|
// Object Functions
|
720
902
|
// ----------------
|
721
903
|
|
722
|
-
//
|
904
|
+
// Keys in IE < 9 that won't be iterated by `for key in ...` and thus missed.
|
905
|
+
var hasEnumBug = !{toString: null}.propertyIsEnumerable('toString');
|
906
|
+
var nonEnumerableProps = ['valueOf', 'isPrototypeOf', 'toString',
|
907
|
+
'propertyIsEnumerable', 'hasOwnProperty', 'toLocaleString'];
|
908
|
+
|
909
|
+
function collectNonEnumProps(obj, keys) {
|
910
|
+
var nonEnumIdx = nonEnumerableProps.length;
|
911
|
+
var constructor = obj.constructor;
|
912
|
+
var proto = (_.isFunction(constructor) && constructor.prototype) || ObjProto;
|
913
|
+
|
914
|
+
// Constructor is a special case.
|
915
|
+
var prop = 'constructor';
|
916
|
+
if (_.has(obj, prop) && !_.contains(keys, prop)) keys.push(prop);
|
917
|
+
|
918
|
+
while (nonEnumIdx--) {
|
919
|
+
prop = nonEnumerableProps[nonEnumIdx];
|
920
|
+
if (prop in obj && obj[prop] !== proto[prop] && !_.contains(keys, prop)) {
|
921
|
+
keys.push(prop);
|
922
|
+
}
|
923
|
+
}
|
924
|
+
}
|
925
|
+
|
926
|
+
// Retrieve the names of an object's own properties.
|
723
927
|
// Delegates to **ECMAScript 5**'s native `Object.keys`
|
724
|
-
_.keys =
|
725
|
-
if (
|
928
|
+
_.keys = function(obj) {
|
929
|
+
if (!_.isObject(obj)) return [];
|
930
|
+
if (nativeKeys) return nativeKeys(obj);
|
726
931
|
var keys = [];
|
727
|
-
for (var key in obj) if (_.has(obj, key)) keys
|
932
|
+
for (var key in obj) if (_.has(obj, key)) keys.push(key);
|
933
|
+
// Ahem, IE < 9.
|
934
|
+
if (hasEnumBug) collectNonEnumProps(obj, keys);
|
935
|
+
return keys;
|
936
|
+
};
|
937
|
+
|
938
|
+
// Retrieve all the property names of an object.
|
939
|
+
_.allKeys = function(obj) {
|
940
|
+
if (!_.isObject(obj)) return [];
|
941
|
+
var keys = [];
|
942
|
+
for (var key in obj) keys.push(key);
|
943
|
+
// Ahem, IE < 9.
|
944
|
+
if (hasEnumBug) collectNonEnumProps(obj, keys);
|
728
945
|
return keys;
|
729
946
|
};
|
730
947
|
|
731
948
|
// Retrieve the values of an object's properties.
|
732
949
|
_.values = function(obj) {
|
733
|
-
var
|
734
|
-
|
950
|
+
var keys = _.keys(obj);
|
951
|
+
var length = keys.length;
|
952
|
+
var values = Array(length);
|
953
|
+
for (var i = 0; i < length; i++) {
|
954
|
+
values[i] = obj[keys[i]];
|
955
|
+
}
|
735
956
|
return values;
|
736
957
|
};
|
737
958
|
|
959
|
+
// Returns the results of applying the iteratee to each element of the object
|
960
|
+
// In contrast to _.map it returns an object
|
961
|
+
_.mapObject = function(obj, iteratee, context) {
|
962
|
+
iteratee = cb(iteratee, context);
|
963
|
+
var keys = _.keys(obj),
|
964
|
+
length = keys.length,
|
965
|
+
results = {},
|
966
|
+
currentKey;
|
967
|
+
for (var index = 0; index < length; index++) {
|
968
|
+
currentKey = keys[index];
|
969
|
+
results[currentKey] = iteratee(obj[currentKey], currentKey, obj);
|
970
|
+
}
|
971
|
+
return results;
|
972
|
+
};
|
973
|
+
|
738
974
|
// Convert an object into a list of `[key, value]` pairs.
|
739
975
|
_.pairs = function(obj) {
|
740
|
-
var
|
741
|
-
|
976
|
+
var keys = _.keys(obj);
|
977
|
+
var length = keys.length;
|
978
|
+
var pairs = Array(length);
|
979
|
+
for (var i = 0; i < length; i++) {
|
980
|
+
pairs[i] = [keys[i], obj[keys[i]]];
|
981
|
+
}
|
742
982
|
return pairs;
|
743
983
|
};
|
744
984
|
|
745
985
|
// Invert the keys and values of an object. The values must be serializable.
|
746
986
|
_.invert = function(obj) {
|
747
987
|
var result = {};
|
748
|
-
|
988
|
+
var keys = _.keys(obj);
|
989
|
+
for (var i = 0, length = keys.length; i < length; i++) {
|
990
|
+
result[obj[keys[i]]] = keys[i];
|
991
|
+
}
|
749
992
|
return result;
|
750
993
|
};
|
751
994
|
|
@@ -760,47 +1003,65 @@
|
|
760
1003
|
};
|
761
1004
|
|
762
1005
|
// Extend a given object with all the properties in passed-in object(s).
|
763
|
-
_.extend =
|
764
|
-
|
765
|
-
|
766
|
-
|
767
|
-
|
768
|
-
|
769
|
-
|
770
|
-
|
771
|
-
|
1006
|
+
_.extend = createAssigner(_.allKeys);
|
1007
|
+
|
1008
|
+
// Assigns a given object with all the own properties in the passed-in object(s)
|
1009
|
+
// (https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Object/assign)
|
1010
|
+
_.extendOwn = _.assign = createAssigner(_.keys);
|
1011
|
+
|
1012
|
+
// Returns the first key on an object that passes a predicate test
|
1013
|
+
_.findKey = function(obj, predicate, context) {
|
1014
|
+
predicate = cb(predicate, context);
|
1015
|
+
var keys = _.keys(obj), key;
|
1016
|
+
for (var i = 0, length = keys.length; i < length; i++) {
|
1017
|
+
key = keys[i];
|
1018
|
+
if (predicate(obj[key], key, obj)) return key;
|
1019
|
+
}
|
772
1020
|
};
|
773
1021
|
|
774
1022
|
// Return a copy of the object only containing the whitelisted properties.
|
775
|
-
_.pick = function(
|
776
|
-
var
|
777
|
-
|
778
|
-
|
779
|
-
|
780
|
-
|
781
|
-
|
1023
|
+
_.pick = function(object, oiteratee, context) {
|
1024
|
+
var result = {}, obj = object, iteratee, keys;
|
1025
|
+
if (obj == null) return result;
|
1026
|
+
if (_.isFunction(oiteratee)) {
|
1027
|
+
keys = _.allKeys(obj);
|
1028
|
+
iteratee = optimizeCb(oiteratee, context);
|
1029
|
+
} else {
|
1030
|
+
keys = flatten(arguments, false, false, 1);
|
1031
|
+
iteratee = function(value, key, obj) { return key in obj; };
|
1032
|
+
obj = Object(obj);
|
1033
|
+
}
|
1034
|
+
for (var i = 0, length = keys.length; i < length; i++) {
|
1035
|
+
var key = keys[i];
|
1036
|
+
var value = obj[key];
|
1037
|
+
if (iteratee(value, key, obj)) result[key] = value;
|
1038
|
+
}
|
1039
|
+
return result;
|
782
1040
|
};
|
783
1041
|
|
784
1042
|
// Return a copy of the object without the blacklisted properties.
|
785
|
-
_.omit = function(obj) {
|
786
|
-
|
787
|
-
|
788
|
-
|
789
|
-
|
1043
|
+
_.omit = function(obj, iteratee, context) {
|
1044
|
+
if (_.isFunction(iteratee)) {
|
1045
|
+
iteratee = _.negate(iteratee);
|
1046
|
+
} else {
|
1047
|
+
var keys = _.map(flatten(arguments, false, false, 1), String);
|
1048
|
+
iteratee = function(value, key) {
|
1049
|
+
return !_.contains(keys, key);
|
1050
|
+
};
|
790
1051
|
}
|
791
|
-
return
|
1052
|
+
return _.pick(obj, iteratee, context);
|
792
1053
|
};
|
793
1054
|
|
794
1055
|
// Fill in a given object with default properties.
|
795
|
-
_.defaults =
|
796
|
-
|
797
|
-
|
798
|
-
|
799
|
-
|
800
|
-
|
801
|
-
|
802
|
-
|
803
|
-
return
|
1056
|
+
_.defaults = createAssigner(_.allKeys, true);
|
1057
|
+
|
1058
|
+
// Creates an object that inherits from the given prototype object.
|
1059
|
+
// If additional properties are provided then they will be added to the
|
1060
|
+
// created object.
|
1061
|
+
_.create = function(prototype, props) {
|
1062
|
+
var result = baseCreate(prototype);
|
1063
|
+
if (props) _.extendOwn(result, props);
|
1064
|
+
return result;
|
804
1065
|
};
|
805
1066
|
|
806
1067
|
// Create a (shallow-cloned) duplicate of an object.
|
@@ -817,11 +1078,24 @@
|
|
817
1078
|
return obj;
|
818
1079
|
};
|
819
1080
|
|
1081
|
+
// Returns whether an object has a given set of `key:value` pairs.
|
1082
|
+
_.isMatch = function(object, attrs) {
|
1083
|
+
var keys = _.keys(attrs), length = keys.length;
|
1084
|
+
if (object == null) return !length;
|
1085
|
+
var obj = Object(object);
|
1086
|
+
for (var i = 0; i < length; i++) {
|
1087
|
+
var key = keys[i];
|
1088
|
+
if (attrs[key] !== obj[key] || !(key in obj)) return false;
|
1089
|
+
}
|
1090
|
+
return true;
|
1091
|
+
};
|
1092
|
+
|
1093
|
+
|
820
1094
|
// Internal recursive comparison function for `isEqual`.
|
821
1095
|
var eq = function(a, b, aStack, bStack) {
|
822
1096
|
// Identical objects are equal. `0 === -0`, but they aren't identical.
|
823
|
-
// See the Harmony `egal` proposal
|
824
|
-
if (a === b) return a !== 0 || 1 / a
|
1097
|
+
// See the [Harmony `egal` proposal](http://wiki.ecmascript.org/doku.php?id=harmony:egal).
|
1098
|
+
if (a === b) return a !== 0 || 1 / a === 1 / b;
|
825
1099
|
// A strict comparison is necessary because `null == undefined`.
|
826
1100
|
if (a == null || b == null) return a === b;
|
827
1101
|
// Unwrap any wrapped objects.
|
@@ -829,97 +1103,98 @@
|
|
829
1103
|
if (b instanceof _) b = b._wrapped;
|
830
1104
|
// Compare `[[Class]]` names.
|
831
1105
|
var className = toString.call(a);
|
832
|
-
if (className
|
1106
|
+
if (className !== toString.call(b)) return false;
|
833
1107
|
switch (className) {
|
834
|
-
// Strings, numbers, dates, and booleans are compared by value.
|
1108
|
+
// Strings, numbers, regular expressions, dates, and booleans are compared by value.
|
1109
|
+
case '[object RegExp]':
|
1110
|
+
// RegExps are coerced to strings for comparison (Note: '' + /a/i === '/a/i')
|
835
1111
|
case '[object String]':
|
836
1112
|
// Primitives and their corresponding object wrappers are equivalent; thus, `"5"` is
|
837
1113
|
// equivalent to `new String("5")`.
|
838
|
-
return a
|
1114
|
+
return '' + a === '' + b;
|
839
1115
|
case '[object Number]':
|
840
|
-
// `NaN`s are equivalent, but non-reflexive.
|
841
|
-
//
|
842
|
-
|
1116
|
+
// `NaN`s are equivalent, but non-reflexive.
|
1117
|
+
// Object(NaN) is equivalent to NaN
|
1118
|
+
if (+a !== +a) return +b !== +b;
|
1119
|
+
// An `egal` comparison is performed for other numeric values.
|
1120
|
+
return +a === 0 ? 1 / +a === 1 / b : +a === +b;
|
843
1121
|
case '[object Date]':
|
844
1122
|
case '[object Boolean]':
|
845
1123
|
// Coerce dates and booleans to numeric primitive values. Dates are compared by their
|
846
1124
|
// millisecond representations. Note that invalid dates with millisecond representations
|
847
1125
|
// of `NaN` are not equivalent.
|
848
|
-
return +a
|
849
|
-
|
850
|
-
|
851
|
-
|
852
|
-
|
853
|
-
|
854
|
-
|
1126
|
+
return +a === +b;
|
1127
|
+
}
|
1128
|
+
|
1129
|
+
var areArrays = className === '[object Array]';
|
1130
|
+
if (!areArrays) {
|
1131
|
+
if (typeof a != 'object' || typeof b != 'object') return false;
|
1132
|
+
|
1133
|
+
// Objects with different constructors are not equivalent, but `Object`s or `Array`s
|
1134
|
+
// from different frames are.
|
1135
|
+
var aCtor = a.constructor, bCtor = b.constructor;
|
1136
|
+
if (aCtor !== bCtor && !(_.isFunction(aCtor) && aCtor instanceof aCtor &&
|
1137
|
+
_.isFunction(bCtor) && bCtor instanceof bCtor)
|
1138
|
+
&& ('constructor' in a && 'constructor' in b)) {
|
1139
|
+
return false;
|
1140
|
+
}
|
855
1141
|
}
|
856
|
-
if (typeof a != 'object' || typeof b != 'object') return false;
|
857
1142
|
// Assume equality for cyclic structures. The algorithm for detecting cyclic
|
858
1143
|
// structures is adapted from ES 5.1 section 15.12.3, abstract operation `JO`.
|
1144
|
+
|
1145
|
+
// Initializing stack of traversed objects.
|
1146
|
+
// It's done here since we only need them for objects and arrays comparison.
|
1147
|
+
aStack = aStack || [];
|
1148
|
+
bStack = bStack || [];
|
859
1149
|
var length = aStack.length;
|
860
1150
|
while (length--) {
|
861
1151
|
// Linear search. Performance is inversely proportional to the number of
|
862
1152
|
// unique nested structures.
|
863
|
-
if (aStack[length]
|
1153
|
+
if (aStack[length] === a) return bStack[length] === b;
|
864
1154
|
}
|
1155
|
+
|
865
1156
|
// Add the first object to the stack of traversed objects.
|
866
1157
|
aStack.push(a);
|
867
1158
|
bStack.push(b);
|
868
|
-
|
1159
|
+
|
869
1160
|
// Recursively compare objects and arrays.
|
870
|
-
if (
|
1161
|
+
if (areArrays) {
|
871
1162
|
// Compare array lengths to determine if a deep comparison is necessary.
|
872
|
-
|
873
|
-
|
874
|
-
|
875
|
-
|
876
|
-
|
877
|
-
if (!(result = eq(a[size], b[size], aStack, bStack))) break;
|
878
|
-
}
|
1163
|
+
length = a.length;
|
1164
|
+
if (length !== b.length) return false;
|
1165
|
+
// Deep compare the contents, ignoring non-numeric properties.
|
1166
|
+
while (length--) {
|
1167
|
+
if (!eq(a[length], b[length], aStack, bStack)) return false;
|
879
1168
|
}
|
880
1169
|
} else {
|
881
|
-
// Objects with different constructors are not equivalent, but `Object`s
|
882
|
-
// from different frames are.
|
883
|
-
var aCtor = a.constructor, bCtor = b.constructor;
|
884
|
-
if (aCtor !== bCtor && !(_.isFunction(aCtor) && (aCtor instanceof aCtor) &&
|
885
|
-
_.isFunction(bCtor) && (bCtor instanceof bCtor))) {
|
886
|
-
return false;
|
887
|
-
}
|
888
1170
|
// Deep compare objects.
|
889
|
-
|
890
|
-
|
891
|
-
|
892
|
-
|
893
|
-
|
894
|
-
|
895
|
-
|
896
|
-
|
897
|
-
// Ensure that both objects contain the same number of properties.
|
898
|
-
if (result) {
|
899
|
-
for (key in b) {
|
900
|
-
if (_.has(b, key) && !(size--)) break;
|
901
|
-
}
|
902
|
-
result = !size;
|
1171
|
+
var keys = _.keys(a), key;
|
1172
|
+
length = keys.length;
|
1173
|
+
// Ensure that both objects contain the same number of properties before comparing deep equality.
|
1174
|
+
if (_.keys(b).length !== length) return false;
|
1175
|
+
while (length--) {
|
1176
|
+
// Deep compare each member
|
1177
|
+
key = keys[length];
|
1178
|
+
if (!(_.has(b, key) && eq(a[key], b[key], aStack, bStack))) return false;
|
903
1179
|
}
|
904
1180
|
}
|
905
1181
|
// Remove the first object from the stack of traversed objects.
|
906
1182
|
aStack.pop();
|
907
1183
|
bStack.pop();
|
908
|
-
return
|
1184
|
+
return true;
|
909
1185
|
};
|
910
1186
|
|
911
1187
|
// Perform a deep comparison to check if two objects are equal.
|
912
1188
|
_.isEqual = function(a, b) {
|
913
|
-
return eq(a, b
|
1189
|
+
return eq(a, b);
|
914
1190
|
};
|
915
1191
|
|
916
1192
|
// Is a given array, string, or object empty?
|
917
1193
|
// An "empty" object has no enumerable own-properties.
|
918
1194
|
_.isEmpty = function(obj) {
|
919
1195
|
if (obj == null) return true;
|
920
|
-
if (_.isArray(obj) || _.isString(obj)) return obj.length === 0;
|
921
|
-
|
922
|
-
return true;
|
1196
|
+
if (isArrayLike(obj) && (_.isArray(obj) || _.isString(obj) || _.isArguments(obj))) return obj.length === 0;
|
1197
|
+
return _.keys(obj).length === 0;
|
923
1198
|
};
|
924
1199
|
|
925
1200
|
// Is a given value a DOM element?
|
@@ -930,33 +1205,35 @@
|
|
930
1205
|
// Is a given value an array?
|
931
1206
|
// Delegates to ECMA5's native Array.isArray
|
932
1207
|
_.isArray = nativeIsArray || function(obj) {
|
933
|
-
return toString.call(obj)
|
1208
|
+
return toString.call(obj) === '[object Array]';
|
934
1209
|
};
|
935
1210
|
|
936
1211
|
// Is a given variable an object?
|
937
1212
|
_.isObject = function(obj) {
|
938
|
-
|
1213
|
+
var type = typeof obj;
|
1214
|
+
return type === 'function' || type === 'object' && !!obj;
|
939
1215
|
};
|
940
1216
|
|
941
|
-
// Add some isType methods: isArguments, isFunction, isString, isNumber, isDate, isRegExp.
|
942
|
-
each(['Arguments', 'Function', 'String', 'Number', 'Date', 'RegExp'], function(name) {
|
1217
|
+
// Add some isType methods: isArguments, isFunction, isString, isNumber, isDate, isRegExp, isError.
|
1218
|
+
_.each(['Arguments', 'Function', 'String', 'Number', 'Date', 'RegExp', 'Error'], function(name) {
|
943
1219
|
_['is' + name] = function(obj) {
|
944
|
-
return toString.call(obj)
|
1220
|
+
return toString.call(obj) === '[object ' + name + ']';
|
945
1221
|
};
|
946
1222
|
});
|
947
1223
|
|
948
|
-
// Define a fallback version of the method in browsers (ahem, IE), where
|
1224
|
+
// Define a fallback version of the method in browsers (ahem, IE < 9), where
|
949
1225
|
// there isn't any inspectable "Arguments" type.
|
950
1226
|
if (!_.isArguments(arguments)) {
|
951
1227
|
_.isArguments = function(obj) {
|
952
|
-
return
|
1228
|
+
return _.has(obj, 'callee');
|
953
1229
|
};
|
954
1230
|
}
|
955
1231
|
|
956
|
-
// Optimize `isFunction` if appropriate.
|
957
|
-
|
1232
|
+
// Optimize `isFunction` if appropriate. Work around some typeof bugs in old v8,
|
1233
|
+
// IE 11 (#1621), and in Safari 8 (#1929).
|
1234
|
+
if (typeof /./ != 'function' && typeof Int8Array != 'object') {
|
958
1235
|
_.isFunction = function(obj) {
|
959
|
-
return typeof obj
|
1236
|
+
return typeof obj == 'function' || false;
|
960
1237
|
};
|
961
1238
|
}
|
962
1239
|
|
@@ -967,12 +1244,12 @@
|
|
967
1244
|
|
968
1245
|
// Is the given value `NaN`? (NaN is the only number which does not equal itself).
|
969
1246
|
_.isNaN = function(obj) {
|
970
|
-
return _.isNumber(obj) && obj
|
1247
|
+
return _.isNumber(obj) && obj !== +obj;
|
971
1248
|
};
|
972
1249
|
|
973
1250
|
// Is a given value a boolean?
|
974
1251
|
_.isBoolean = function(obj) {
|
975
|
-
return obj === true || obj === false || toString.call(obj)
|
1252
|
+
return obj === true || obj === false || toString.call(obj) === '[object Boolean]';
|
976
1253
|
};
|
977
1254
|
|
978
1255
|
// Is a given value equal to null?
|
@@ -988,7 +1265,7 @@
|
|
988
1265
|
// Shortcut function for checking if an object has a given property directly
|
989
1266
|
// on itself (in other words, not on a prototype).
|
990
1267
|
_.has = function(obj, key) {
|
991
|
-
return hasOwnProperty.call(obj, key);
|
1268
|
+
return obj != null && hasOwnProperty.call(obj, key);
|
992
1269
|
};
|
993
1270
|
|
994
1271
|
// Utility Functions
|
@@ -1001,15 +1278,43 @@
|
|
1001
1278
|
return this;
|
1002
1279
|
};
|
1003
1280
|
|
1004
|
-
// Keep the identity function around for default
|
1281
|
+
// Keep the identity function around for default iteratees.
|
1005
1282
|
_.identity = function(value) {
|
1006
1283
|
return value;
|
1007
1284
|
};
|
1008
1285
|
|
1286
|
+
// Predicate-generating functions. Often useful outside of Underscore.
|
1287
|
+
_.constant = function(value) {
|
1288
|
+
return function() {
|
1289
|
+
return value;
|
1290
|
+
};
|
1291
|
+
};
|
1292
|
+
|
1293
|
+
_.noop = function(){};
|
1294
|
+
|
1295
|
+
_.property = property;
|
1296
|
+
|
1297
|
+
// Generates a function for a given object that returns a given property.
|
1298
|
+
_.propertyOf = function(obj) {
|
1299
|
+
return obj == null ? function(){} : function(key) {
|
1300
|
+
return obj[key];
|
1301
|
+
};
|
1302
|
+
};
|
1303
|
+
|
1304
|
+
// Returns a predicate for checking whether an object has a given set of
|
1305
|
+
// `key:value` pairs.
|
1306
|
+
_.matcher = _.matches = function(attrs) {
|
1307
|
+
attrs = _.extendOwn({}, attrs);
|
1308
|
+
return function(obj) {
|
1309
|
+
return _.isMatch(obj, attrs);
|
1310
|
+
};
|
1311
|
+
};
|
1312
|
+
|
1009
1313
|
// Run a function **n** times.
|
1010
|
-
_.times = function(n,
|
1011
|
-
var accum = Array(n);
|
1012
|
-
|
1314
|
+
_.times = function(n, iteratee, context) {
|
1315
|
+
var accum = Array(Math.max(0, n));
|
1316
|
+
iteratee = optimizeCb(iteratee, context, 1);
|
1317
|
+
for (var i = 0; i < n; i++) accum[i] = iteratee(i);
|
1013
1318
|
return accum;
|
1014
1319
|
};
|
1015
1320
|
|
@@ -1019,63 +1324,57 @@
|
|
1019
1324
|
max = min;
|
1020
1325
|
min = 0;
|
1021
1326
|
}
|
1022
|
-
return min + (
|
1023
|
-
};
|
1024
|
-
|
1025
|
-
//
|
1026
|
-
|
1027
|
-
|
1028
|
-
'&': '&',
|
1029
|
-
'<': '<',
|
1030
|
-
'>': '>',
|
1031
|
-
'"': '"',
|
1032
|
-
"'": ''',
|
1033
|
-
'/': '/'
|
1034
|
-
}
|
1327
|
+
return min + Math.floor(Math.random() * (max - min + 1));
|
1328
|
+
};
|
1329
|
+
|
1330
|
+
// A (possibly faster) way to get the current timestamp as an integer.
|
1331
|
+
_.now = Date.now || function() {
|
1332
|
+
return new Date().getTime();
|
1035
1333
|
};
|
1036
|
-
entityMap.unescape = _.invert(entityMap.escape);
|
1037
1334
|
|
1038
|
-
|
1039
|
-
var
|
1040
|
-
|
1041
|
-
|
1335
|
+
// List of HTML entities for escaping.
|
1336
|
+
var escapeMap = {
|
1337
|
+
'&': '&',
|
1338
|
+
'<': '<',
|
1339
|
+
'>': '>',
|
1340
|
+
'"': '"',
|
1341
|
+
"'": ''',
|
1342
|
+
'`': '`'
|
1042
1343
|
};
|
1344
|
+
var unescapeMap = _.invert(escapeMap);
|
1043
1345
|
|
1044
1346
|
// Functions for escaping and unescaping strings to/from HTML interpolation.
|
1045
|
-
|
1046
|
-
|
1047
|
-
|
1048
|
-
|
1049
|
-
|
1050
|
-
|
1347
|
+
var createEscaper = function(map) {
|
1348
|
+
var escaper = function(match) {
|
1349
|
+
return map[match];
|
1350
|
+
};
|
1351
|
+
// Regexes for identifying a key that needs to be escaped
|
1352
|
+
var source = '(?:' + _.keys(map).join('|') + ')';
|
1353
|
+
var testRegexp = RegExp(source);
|
1354
|
+
var replaceRegexp = RegExp(source, 'g');
|
1355
|
+
return function(string) {
|
1356
|
+
string = string == null ? '' : '' + string;
|
1357
|
+
return testRegexp.test(string) ? string.replace(replaceRegexp, escaper) : string;
|
1051
1358
|
};
|
1052
|
-
});
|
1053
|
-
|
1054
|
-
// If the value of the named property is a function then invoke it;
|
1055
|
-
// otherwise, return it.
|
1056
|
-
_.result = function(object, property) {
|
1057
|
-
if (object == null) return null;
|
1058
|
-
var value = object[property];
|
1059
|
-
return _.isFunction(value) ? value.call(object) : value;
|
1060
1359
|
};
|
1360
|
+
_.escape = createEscaper(escapeMap);
|
1361
|
+
_.unescape = createEscaper(unescapeMap);
|
1061
1362
|
|
1062
|
-
//
|
1063
|
-
|
1064
|
-
|
1065
|
-
|
1066
|
-
|
1067
|
-
|
1068
|
-
|
1069
|
-
|
1070
|
-
};
|
1071
|
-
});
|
1363
|
+
// If the value of the named `property` is a function then invoke it with the
|
1364
|
+
// `object` as context; otherwise, return it.
|
1365
|
+
_.result = function(object, property, fallback) {
|
1366
|
+
var value = object == null ? void 0 : object[property];
|
1367
|
+
if (value === void 0) {
|
1368
|
+
value = fallback;
|
1369
|
+
}
|
1370
|
+
return _.isFunction(value) ? value.call(object) : value;
|
1072
1371
|
};
|
1073
1372
|
|
1074
1373
|
// Generate a unique integer id (unique within the entire client session).
|
1075
1374
|
// Useful for temporary DOM ids.
|
1076
1375
|
var idCounter = 0;
|
1077
1376
|
_.uniqueId = function(prefix) {
|
1078
|
-
var id =
|
1377
|
+
var id = ++idCounter + '';
|
1079
1378
|
return prefix ? prefix + id : id;
|
1080
1379
|
};
|
1081
1380
|
|
@@ -1099,21 +1398,26 @@
|
|
1099
1398
|
'\\': '\\',
|
1100
1399
|
'\r': 'r',
|
1101
1400
|
'\n': 'n',
|
1102
|
-
'\t': 't',
|
1103
1401
|
'\u2028': 'u2028',
|
1104
1402
|
'\u2029': 'u2029'
|
1105
1403
|
};
|
1106
1404
|
|
1107
|
-
var escaper = /\\|'|\r|\n|\
|
1405
|
+
var escaper = /\\|'|\r|\n|\u2028|\u2029/g;
|
1406
|
+
|
1407
|
+
var escapeChar = function(match) {
|
1408
|
+
return '\\' + escapes[match];
|
1409
|
+
};
|
1108
1410
|
|
1109
1411
|
// JavaScript micro-templating, similar to John Resig's implementation.
|
1110
1412
|
// Underscore templating handles arbitrary delimiters, preserves whitespace,
|
1111
1413
|
// and correctly escapes quotes within interpolated code.
|
1112
|
-
|
1414
|
+
// NB: `oldSettings` only exists for backwards compatibility.
|
1415
|
+
_.template = function(text, settings, oldSettings) {
|
1416
|
+
if (!settings && oldSettings) settings = oldSettings;
|
1113
1417
|
settings = _.defaults({}, settings, _.templateSettings);
|
1114
1418
|
|
1115
1419
|
// Combine delimiters into one regular expression via alternation.
|
1116
|
-
var matcher =
|
1420
|
+
var matcher = RegExp([
|
1117
1421
|
(settings.escape || noMatch).source,
|
1118
1422
|
(settings.interpolate || noMatch).source,
|
1119
1423
|
(settings.evaluate || noMatch).source
|
@@ -1123,19 +1427,18 @@
|
|
1123
1427
|
var index = 0;
|
1124
1428
|
var source = "__p+='";
|
1125
1429
|
text.replace(matcher, function(match, escape, interpolate, evaluate, offset) {
|
1126
|
-
source += text.slice(index, offset)
|
1127
|
-
|
1430
|
+
source += text.slice(index, offset).replace(escaper, escapeChar);
|
1431
|
+
index = offset + match.length;
|
1128
1432
|
|
1129
1433
|
if (escape) {
|
1130
1434
|
source += "'+\n((__t=(" + escape + "))==null?'':_.escape(__t))+\n'";
|
1131
|
-
}
|
1132
|
-
if (interpolate) {
|
1435
|
+
} else if (interpolate) {
|
1133
1436
|
source += "'+\n((__t=(" + interpolate + "))==null?'':__t)+\n'";
|
1134
|
-
}
|
1135
|
-
if (evaluate) {
|
1437
|
+
} else if (evaluate) {
|
1136
1438
|
source += "';\n" + evaluate + "\n__p+='";
|
1137
1439
|
}
|
1138
|
-
|
1440
|
+
|
1441
|
+
// Adobe VMs need the match returned to produce the correct offest.
|
1139
1442
|
return match;
|
1140
1443
|
});
|
1141
1444
|
source += "';\n";
|
@@ -1145,7 +1448,7 @@
|
|
1145
1448
|
|
1146
1449
|
source = "var __t,__p='',__j=Array.prototype.join," +
|
1147
1450
|
"print=function(){__p+=__j.call(arguments,'');};\n" +
|
1148
|
-
source +
|
1451
|
+
source + 'return __p;\n';
|
1149
1452
|
|
1150
1453
|
try {
|
1151
1454
|
var render = new Function(settings.variable || 'obj', '_', source);
|
@@ -1154,20 +1457,22 @@
|
|
1154
1457
|
throw e;
|
1155
1458
|
}
|
1156
1459
|
|
1157
|
-
if (data) return render(data, _);
|
1158
1460
|
var template = function(data) {
|
1159
1461
|
return render.call(this, data, _);
|
1160
1462
|
};
|
1161
1463
|
|
1162
|
-
// Provide the compiled
|
1163
|
-
|
1464
|
+
// Provide the compiled source as a convenience for precompilation.
|
1465
|
+
var argument = settings.variable || 'obj';
|
1466
|
+
template.source = 'function(' + argument + '){\n' + source + '}';
|
1164
1467
|
|
1165
1468
|
return template;
|
1166
1469
|
};
|
1167
1470
|
|
1168
|
-
// Add a "chain" function
|
1471
|
+
// Add a "chain" function. Start chaining a wrapped Underscore object.
|
1169
1472
|
_.chain = function(obj) {
|
1170
|
-
|
1473
|
+
var instance = _(obj);
|
1474
|
+
instance._chain = true;
|
1475
|
+
return instance;
|
1171
1476
|
};
|
1172
1477
|
|
1173
1478
|
// OOP
|
@@ -1177,45 +1482,67 @@
|
|
1177
1482
|
// underscore functions. Wrapped objects may be chained.
|
1178
1483
|
|
1179
1484
|
// Helper function to continue chaining intermediate results.
|
1180
|
-
var result = function(obj) {
|
1181
|
-
return
|
1485
|
+
var result = function(instance, obj) {
|
1486
|
+
return instance._chain ? _(obj).chain() : obj;
|
1487
|
+
};
|
1488
|
+
|
1489
|
+
// Add your own custom functions to the Underscore object.
|
1490
|
+
_.mixin = function(obj) {
|
1491
|
+
_.each(_.functions(obj), function(name) {
|
1492
|
+
var func = _[name] = obj[name];
|
1493
|
+
_.prototype[name] = function() {
|
1494
|
+
var args = [this._wrapped];
|
1495
|
+
push.apply(args, arguments);
|
1496
|
+
return result(this, func.apply(_, args));
|
1497
|
+
};
|
1498
|
+
});
|
1182
1499
|
};
|
1183
1500
|
|
1184
1501
|
// Add all of the Underscore functions to the wrapper object.
|
1185
1502
|
_.mixin(_);
|
1186
1503
|
|
1187
1504
|
// Add all mutator Array functions to the wrapper.
|
1188
|
-
each(['pop', 'push', 'reverse', 'shift', 'sort', 'splice', 'unshift'], function(name) {
|
1505
|
+
_.each(['pop', 'push', 'reverse', 'shift', 'sort', 'splice', 'unshift'], function(name) {
|
1189
1506
|
var method = ArrayProto[name];
|
1190
1507
|
_.prototype[name] = function() {
|
1191
1508
|
var obj = this._wrapped;
|
1192
1509
|
method.apply(obj, arguments);
|
1193
|
-
if ((name
|
1194
|
-
return result
|
1510
|
+
if ((name === 'shift' || name === 'splice') && obj.length === 0) delete obj[0];
|
1511
|
+
return result(this, obj);
|
1195
1512
|
};
|
1196
1513
|
});
|
1197
1514
|
|
1198
1515
|
// Add all accessor Array functions to the wrapper.
|
1199
|
-
each(['concat', 'join', 'slice'], function(name) {
|
1516
|
+
_.each(['concat', 'join', 'slice'], function(name) {
|
1200
1517
|
var method = ArrayProto[name];
|
1201
1518
|
_.prototype[name] = function() {
|
1202
|
-
return result
|
1519
|
+
return result(this, method.apply(this._wrapped, arguments));
|
1203
1520
|
};
|
1204
1521
|
});
|
1205
1522
|
|
1206
|
-
|
1207
|
-
|
1208
|
-
|
1209
|
-
|
1210
|
-
this._chain = true;
|
1211
|
-
return this;
|
1212
|
-
},
|
1523
|
+
// Extracts the result from a wrapped and chained object.
|
1524
|
+
_.prototype.value = function() {
|
1525
|
+
return this._wrapped;
|
1526
|
+
};
|
1213
1527
|
|
1214
|
-
|
1215
|
-
|
1216
|
-
|
1217
|
-
}
|
1528
|
+
// Provide unwrapping proxy for some methods used in engine operations
|
1529
|
+
// such as arithmetic and JSON stringification.
|
1530
|
+
_.prototype.valueOf = _.prototype.toJSON = _.prototype.value;
|
1218
1531
|
|
1219
|
-
|
1532
|
+
_.prototype.toString = function() {
|
1533
|
+
return '' + this._wrapped;
|
1534
|
+
};
|
1220
1535
|
|
1221
|
-
|
1536
|
+
// AMD registration happens at the end for compatibility with AMD loaders
|
1537
|
+
// that may not enforce next-turn semantics on modules. Even though general
|
1538
|
+
// practice for AMD registration is to be anonymous, underscore registers
|
1539
|
+
// as a named module because, like jQuery, it is a base library that is
|
1540
|
+
// popular enough to be bundled in a third party lib, but not be part of
|
1541
|
+
// an AMD load request. Those cases could generate an error when an
|
1542
|
+
// anonymous define() is called outside of a loader request.
|
1543
|
+
if (typeof define === 'function' && define.amd) {
|
1544
|
+
define('underscore', [], function() {
|
1545
|
+
return _;
|
1546
|
+
});
|
1547
|
+
}
|
1548
|
+
}.call(this));
|