rails-backbone 0.7.2 → 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 +68 -22
- data/Rakefile +1 -0
- data/lib/generators/backbone/install/install_generator.rb +33 -11
- data/lib/generators/backbone/router/templates/view.coffee +1 -1
- data/lib/generators/backbone/scaffold/templates/router.coffee +1 -1
- data/lib/generators/backbone/scaffold/templates/templates/edit.jst +1 -1
- data/lib/generators/backbone/scaffold/templates/templates/model.jst +3 -3
- data/lib/generators/backbone/scaffold/templates/views/edit_view.coffee +7 -7
- data/lib/generators/backbone/scaffold/templates/views/index_view.coffee +3 -3
- data/lib/generators/backbone/scaffold/templates/views/model_view.coffee +1 -1
- data/lib/generators/backbone/scaffold/templates/views/new_view.coffee +1 -1
- data/lib/generators/backbone/scaffold/templates/views/show_view.coffee +1 -1
- data/vendor/assets/javascripts/backbone.js +1301 -864
- data/vendor/assets/javascripts/backbone_rails_sync.js +19 -55
- data/vendor/assets/javascripts/underscore.js +1067 -578
- metadata +140 -35
@@ -1,68 +1,32 @@
|
|
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
|
-
};
|
1
|
+
(function($) {
|
2
|
+
Backbone._sync = Backbone.sync;
|
17
3
|
|
18
4
|
Backbone.sync = function(method, model, options) {
|
19
|
-
|
5
|
+
if (!options.noCSRF) {
|
6
|
+
var beforeSend = options.beforeSend;
|
20
7
|
|
21
|
-
|
22
|
-
|
23
|
-
type: type,
|
24
|
-
dataType: 'json',
|
25
|
-
beforeSend: function( xhr ) {
|
8
|
+
// Set X-CSRF-Token HTTP header
|
9
|
+
options.beforeSend = function(xhr) {
|
26
10
|
var token = $('meta[name="csrf-token"]').attr('content');
|
27
11
|
if (token) xhr.setRequestHeader('X-CSRF-Token', token);
|
28
|
-
|
29
|
-
|
30
|
-
}
|
31
|
-
}, options);
|
32
|
-
|
33
|
-
if (!params.url) {
|
34
|
-
params.url = getUrl(model) || urlError();
|
12
|
+
if (beforeSend) return beforeSend.apply(this, arguments);
|
13
|
+
};
|
35
14
|
}
|
36
15
|
|
37
|
-
//
|
38
|
-
if (
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
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);
|
45
23
|
} else {
|
46
24
|
data = model.toJSON();
|
47
25
|
}
|
48
|
-
|
49
|
-
params.data = JSON.stringify(data)
|
26
|
+
options.data = JSON.stringify(data);
|
50
27
|
}
|
51
28
|
|
52
|
-
|
53
|
-
|
54
|
-
params.processData = false;
|
55
|
-
}
|
29
|
+
return Backbone._sync(method, model, options);
|
30
|
+
};
|
56
31
|
|
57
|
-
|
58
|
-
var complete = options.complete;
|
59
|
-
params.complete = function(jqXHR, textStatus) {
|
60
|
-
model.trigger('sync:end');
|
61
|
-
if (complete) complete(jqXHR, textStatus);
|
62
|
-
};
|
63
|
-
|
64
|
-
// Make the request.
|
65
|
-
return $.ajax(params);
|
66
|
-
}
|
67
|
-
|
68
|
-
}).call(this);
|
32
|
+
})(jQuery);
|
@@ -1,314 +1,450 @@
|
|
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.8.3
|
2
|
+
// http://underscorejs.org
|
3
|
+
// (c) 2009-2015 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.
|
18
15
|
var previousUnderscore = root._;
|
19
16
|
|
20
|
-
// Establish the object that gets returned to break out of a loop iteration.
|
21
|
-
var breaker = {};
|
22
|
-
|
23
17
|
// Save bytes in the minified (but not gzipped) version:
|
24
18
|
var ArrayProto = Array.prototype, ObjProto = Object.prototype, FuncProto = Function.prototype;
|
25
19
|
|
26
20
|
// Create quick reference variables for speed access to core prototypes.
|
27
|
-
var
|
28
|
-
|
29
|
-
|
30
|
-
|
21
|
+
var
|
22
|
+
push = ArrayProto.push,
|
23
|
+
slice = ArrayProto.slice,
|
24
|
+
toString = ObjProto.toString,
|
25
|
+
hasOwnProperty = ObjProto.hasOwnProperty;
|
31
26
|
|
32
27
|
// All **ECMAScript 5** native function implementations that we hope to use
|
33
28
|
// are declared here.
|
34
29
|
var
|
35
|
-
nativeForEach = ArrayProto.forEach,
|
36
|
-
nativeMap = ArrayProto.map,
|
37
|
-
nativeReduce = ArrayProto.reduce,
|
38
|
-
nativeReduceRight = ArrayProto.reduceRight,
|
39
|
-
nativeFilter = ArrayProto.filter,
|
40
|
-
nativeEvery = ArrayProto.every,
|
41
|
-
nativeSome = ArrayProto.some,
|
42
|
-
nativeIndexOf = ArrayProto.indexOf,
|
43
|
-
nativeLastIndexOf = ArrayProto.lastIndexOf,
|
44
30
|
nativeIsArray = Array.isArray,
|
45
31
|
nativeKeys = Object.keys,
|
46
|
-
nativeBind = FuncProto.bind
|
32
|
+
nativeBind = FuncProto.bind,
|
33
|
+
nativeCreate = Object.create;
|
34
|
+
|
35
|
+
// Naked function reference for surrogate-prototype-swapping.
|
36
|
+
var Ctor = function(){};
|
47
37
|
|
48
38
|
// Create a safe reference to the Underscore object for use below.
|
49
|
-
var _ = function(obj) {
|
39
|
+
var _ = function(obj) {
|
40
|
+
if (obj instanceof _) return obj;
|
41
|
+
if (!(this instanceof _)) return new _(obj);
|
42
|
+
this._wrapped = obj;
|
43
|
+
};
|
50
44
|
|
51
45
|
// Export the Underscore object for **Node.js**, with
|
52
46
|
// backwards-compatibility for the old `require()` API. If we're in
|
53
|
-
// the browser, add `_` as a global object
|
54
|
-
// for Closure Compiler "advanced" mode.
|
47
|
+
// the browser, add `_` as a global object.
|
55
48
|
if (typeof exports !== 'undefined') {
|
56
49
|
if (typeof module !== 'undefined' && module.exports) {
|
57
50
|
exports = module.exports = _;
|
58
51
|
}
|
59
52
|
exports._ = _;
|
60
53
|
} else {
|
61
|
-
root
|
54
|
+
root._ = _;
|
62
55
|
}
|
63
56
|
|
64
57
|
// Current version.
|
65
|
-
_.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
|
+
};
|
66
141
|
|
67
142
|
// Collection Functions
|
68
143
|
// --------------------
|
69
144
|
|
70
145
|
// The cornerstone, an `each` implementation, aka `forEach`.
|
71
|
-
// Handles objects
|
72
|
-
//
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
if (i in obj && 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);
|
80
154
|
}
|
81
155
|
} else {
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
}
|
156
|
+
var keys = _.keys(obj);
|
157
|
+
for (i = 0, length = keys.length; i < length; i++) {
|
158
|
+
iteratee(obj[keys[i]], keys[i], obj);
|
86
159
|
}
|
87
160
|
}
|
161
|
+
return obj;
|
88
162
|
};
|
89
163
|
|
90
|
-
// Return the results of applying the
|
91
|
-
|
92
|
-
|
93
|
-
var
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
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
|
+
}
|
100
174
|
return results;
|
101
175
|
};
|
102
176
|
|
103
|
-
//
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
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;
|
111
187
|
}
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
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;
|
118
198
|
}
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
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);
|
123
206
|
|
124
207
|
// The right-associative version of reduce, also known as `foldr`.
|
125
|
-
|
126
|
-
_.reduceRight = _.foldr = function(obj, iterator, memo, context) {
|
127
|
-
var initial = arguments.length > 2;
|
128
|
-
if (obj == null) obj = [];
|
129
|
-
if (nativeReduceRight && obj.reduceRight === nativeReduceRight) {
|
130
|
-
if (context) iterator = _.bind(iterator, context);
|
131
|
-
return initial ? obj.reduceRight(iterator, memo) : obj.reduceRight(iterator);
|
132
|
-
}
|
133
|
-
var reversed = _.toArray(obj).reverse();
|
134
|
-
if (context && !initial) iterator = _.bind(iterator, context);
|
135
|
-
return initial ? _.reduce(reversed, iterator, memo, context) : _.reduce(reversed, iterator);
|
136
|
-
};
|
208
|
+
_.reduceRight = _.foldr = createReduce(-1);
|
137
209
|
|
138
210
|
// Return the first value which passes a truth test. Aliased as `detect`.
|
139
|
-
_.find = _.detect = function(obj,
|
140
|
-
var
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
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];
|
148
219
|
};
|
149
220
|
|
150
221
|
// Return all the elements that pass a truth test.
|
151
|
-
// Delegates to **ECMAScript 5**'s native `filter` if available.
|
152
222
|
// Aliased as `select`.
|
153
|
-
_.filter = _.select = function(obj,
|
223
|
+
_.filter = _.select = function(obj, predicate, context) {
|
154
224
|
var results = [];
|
155
|
-
|
156
|
-
|
157
|
-
|
158
|
-
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);
|
159
228
|
});
|
160
229
|
return results;
|
161
230
|
};
|
162
231
|
|
163
232
|
// Return all the elements for which a truth test fails.
|
164
|
-
_.reject = function(obj,
|
165
|
-
|
166
|
-
if (obj == null) return results;
|
167
|
-
each(obj, function(value, index, list) {
|
168
|
-
if (!iterator.call(context, value, index, list)) results[results.length] = value;
|
169
|
-
});
|
170
|
-
return results;
|
233
|
+
_.reject = function(obj, predicate, context) {
|
234
|
+
return _.filter(obj, _.negate(cb(predicate)), context);
|
171
235
|
};
|
172
236
|
|
173
237
|
// Determine whether all of the elements match a truth test.
|
174
|
-
// Delegates to **ECMAScript 5**'s native `every` if available.
|
175
238
|
// Aliased as `all`.
|
176
|
-
_.every = _.all = function(obj,
|
177
|
-
|
178
|
-
|
179
|
-
|
180
|
-
|
181
|
-
|
182
|
-
|
183
|
-
|
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;
|
184
248
|
};
|
185
249
|
|
186
250
|
// Determine if at least one element in the object matches a truth test.
|
187
|
-
// Delegates to **ECMAScript 5**'s native `some` if available.
|
188
251
|
// Aliased as `any`.
|
189
|
-
|
190
|
-
|
191
|
-
var
|
192
|
-
|
193
|
-
|
194
|
-
|
195
|
-
if (
|
196
|
-
}
|
197
|
-
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;
|
198
261
|
};
|
199
262
|
|
200
|
-
// Determine if
|
201
|
-
// Aliased as `
|
202
|
-
_.
|
203
|
-
|
204
|
-
if (
|
205
|
-
|
206
|
-
found = any(obj, function(value) {
|
207
|
-
return value === target;
|
208
|
-
});
|
209
|
-
return found;
|
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;
|
210
269
|
};
|
211
270
|
|
212
271
|
// Invoke a method (with arguments) on every item in a collection.
|
213
272
|
_.invoke = function(obj, method) {
|
214
273
|
var args = slice.call(arguments, 2);
|
274
|
+
var isFunc = _.isFunction(method);
|
215
275
|
return _.map(obj, function(value) {
|
216
|
-
|
276
|
+
var func = isFunc ? method : value[method];
|
277
|
+
return func == null ? func : func.apply(value, args);
|
217
278
|
});
|
218
279
|
};
|
219
280
|
|
220
281
|
// Convenience version of a common use case of `map`: fetching a property.
|
221
282
|
_.pluck = function(obj, key) {
|
222
|
-
return _.map(obj,
|
283
|
+
return _.map(obj, _.property(key));
|
223
284
|
};
|
224
285
|
|
225
|
-
//
|
226
|
-
|
227
|
-
|
228
|
-
|
229
|
-
|
230
|
-
|
231
|
-
|
232
|
-
|
233
|
-
|
234
|
-
return
|
286
|
+
// Convenience version of a common use case of `filter`: selecting only objects
|
287
|
+
// containing specific `key:value` pairs.
|
288
|
+
_.where = function(obj, attrs) {
|
289
|
+
return _.filter(obj, _.matcher(attrs));
|
290
|
+
};
|
291
|
+
|
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
|
+
});
|
319
|
+
}
|
320
|
+
return result;
|
235
321
|
};
|
236
322
|
|
237
323
|
// Return the minimum element (or element-based computation).
|
238
|
-
_.min = function(obj,
|
239
|
-
|
240
|
-
|
241
|
-
|
242
|
-
|
243
|
-
var
|
244
|
-
|
245
|
-
|
246
|
-
|
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
|
+
});
|
344
|
+
}
|
345
|
+
return result;
|
247
346
|
};
|
248
347
|
|
249
|
-
// Shuffle
|
348
|
+
// Shuffle a collection, using the modern version of the
|
349
|
+
// [Fisher-Yates shuffle](http://en.wikipedia.org/wiki/Fisher–Yates_shuffle).
|
250
350
|
_.shuffle = function(obj) {
|
251
|
-
var
|
252
|
-
|
253
|
-
|
254
|
-
|
255
|
-
|
256
|
-
|
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
|
+
}
|
257
359
|
return shuffled;
|
258
360
|
};
|
259
361
|
|
260
|
-
//
|
261
|
-
|
262
|
-
|
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));
|
371
|
+
};
|
372
|
+
|
373
|
+
// Sort the object's values by a criterion produced by an iteratee.
|
374
|
+
_.sortBy = function(obj, iteratee, context) {
|
375
|
+
iteratee = cb(iteratee, context);
|
263
376
|
return _.pluck(_.map(obj, function(value, index, list) {
|
264
377
|
return {
|
265
|
-
value
|
266
|
-
|
378
|
+
value: value,
|
379
|
+
index: index,
|
380
|
+
criteria: iteratee(value, index, list)
|
267
381
|
};
|
268
382
|
}).sort(function(left, right) {
|
269
|
-
var a = left.criteria
|
270
|
-
|
271
|
-
if (
|
272
|
-
|
383
|
+
var a = left.criteria;
|
384
|
+
var b = right.criteria;
|
385
|
+
if (a !== b) {
|
386
|
+
if (a > b || a === void 0) return 1;
|
387
|
+
if (a < b || b === void 0) return -1;
|
388
|
+
}
|
389
|
+
return left.index - right.index;
|
273
390
|
}), 'value');
|
274
391
|
};
|
275
392
|
|
393
|
+
// An internal function used for aggregate "group by" operations.
|
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
|
+
};
|
404
|
+
};
|
405
|
+
|
276
406
|
// Groups the object's values by a criterion. Pass either a string attribute
|
277
407
|
// 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
|
-
};
|
408
|
+
_.groupBy = group(function(result, value, key) {
|
409
|
+
if (_.has(result, key)) result[key].push(value); else result[key] = [value];
|
410
|
+
});
|
287
411
|
|
288
|
-
//
|
289
|
-
//
|
290
|
-
_.
|
291
|
-
|
292
|
-
|
293
|
-
while (low < high) {
|
294
|
-
var mid = (low + high) >> 1;
|
295
|
-
iterator(array[mid]) < iterator(obj) ? low = mid + 1 : high = mid;
|
296
|
-
}
|
297
|
-
return low;
|
298
|
-
};
|
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
|
+
});
|
299
417
|
|
300
|
-
//
|
418
|
+
// Counts instances of an object that group by a certain criterion. Pass
|
419
|
+
// either a string attribute to count by, or a function that returns the
|
420
|
+
// criterion.
|
421
|
+
_.countBy = group(function(result, value, key) {
|
422
|
+
if (_.has(result, key)) result[key]++; else result[key] = 1;
|
423
|
+
});
|
424
|
+
|
425
|
+
// Safely create a real, live array from anything iterable.
|
301
426
|
_.toArray = function(obj) {
|
302
|
-
if (!obj)
|
303
|
-
if (_.isArray(obj))
|
304
|
-
if (
|
305
|
-
if (obj.toArray && _.isFunction(obj.toArray)) return obj.toArray();
|
427
|
+
if (!obj) return [];
|
428
|
+
if (_.isArray(obj)) return slice.call(obj);
|
429
|
+
if (isArrayLike(obj)) return _.map(obj, _.identity);
|
306
430
|
return _.values(obj);
|
307
431
|
};
|
308
432
|
|
309
433
|
// Return the number of elements in an object.
|
310
434
|
_.size = function(obj) {
|
311
|
-
|
435
|
+
if (obj == null) return 0;
|
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];
|
312
448
|
};
|
313
449
|
|
314
450
|
// Array Functions
|
@@ -318,47 +454,61 @@
|
|
318
454
|
// values in the array. Aliased as `head` and `take`. The **guard** check
|
319
455
|
// allows it to work with `_.map`.
|
320
456
|
_.first = _.head = _.take = function(array, n, guard) {
|
321
|
-
|
457
|
+
if (array == null) return void 0;
|
458
|
+
if (n == null || guard) return array[0];
|
459
|
+
return _.initial(array, array.length - n);
|
322
460
|
};
|
323
461
|
|
324
|
-
// Returns everything but the last entry of the array.
|
462
|
+
// Returns everything but the last entry of the array. Especially useful on
|
325
463
|
// the arguments object. Passing **n** will return all the values in
|
326
|
-
// the array, excluding the last N.
|
327
|
-
// `_.map`.
|
464
|
+
// the array, excluding the last N.
|
328
465
|
_.initial = function(array, n, guard) {
|
329
|
-
return slice.call(array, 0, array.length - (
|
466
|
+
return slice.call(array, 0, Math.max(0, array.length - (n == null || guard ? 1 : n)));
|
330
467
|
};
|
331
468
|
|
332
469
|
// Get the last element of an array. Passing **n** will return the last N
|
333
|
-
// values in the array.
|
470
|
+
// values in the array.
|
334
471
|
_.last = function(array, n, guard) {
|
335
|
-
if (
|
336
|
-
|
337
|
-
|
338
|
-
return array[array.length - 1];
|
339
|
-
}
|
472
|
+
if (array == null) return void 0;
|
473
|
+
if (n == null || guard) return array[array.length - 1];
|
474
|
+
return _.rest(array, Math.max(0, array.length - n));
|
340
475
|
};
|
341
476
|
|
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
|
345
|
-
|
346
|
-
|
347
|
-
return slice.call(array, (index == null) || guard ? 1 : index);
|
477
|
+
// Returns everything but the first entry of the array. Aliased as `tail` and `drop`.
|
478
|
+
// Especially useful on the arguments object. Passing an **n** will return
|
479
|
+
// the rest N values in the array.
|
480
|
+
_.rest = _.tail = _.drop = function(array, n, guard) {
|
481
|
+
return slice.call(array, n == null || guard ? 1 : n);
|
348
482
|
};
|
349
483
|
|
350
484
|
// Trim out all falsy values from an array.
|
351
485
|
_.compact = function(array) {
|
352
|
-
return _.filter(array,
|
486
|
+
return _.filter(array, _.identity);
|
487
|
+
};
|
488
|
+
|
489
|
+
// Internal implementation of a recursive `flatten` function.
|
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;
|
504
|
+
}
|
505
|
+
}
|
506
|
+
return output;
|
353
507
|
};
|
354
508
|
|
355
|
-
//
|
509
|
+
// Flatten out an array, either recursively (by default), or just one level.
|
356
510
|
_.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
|
-
}, []);
|
511
|
+
return flatten(array, shallow, false);
|
362
512
|
};
|
363
513
|
|
364
514
|
// Return a version of the array that does not contain the specified value(s).
|
@@ -369,99 +519,174 @@
|
|
369
519
|
// Produce a duplicate-free version of the array. If the array has already
|
370
520
|
// been sorted, you have the option of using a faster algorithm.
|
371
521
|
// Aliased as `unique`.
|
372
|
-
_.uniq = _.unique = function(array, isSorted,
|
373
|
-
|
374
|
-
|
375
|
-
|
376
|
-
|
377
|
-
|
378
|
-
|
379
|
-
|
380
|
-
|
522
|
+
_.uniq = _.unique = function(array, isSorted, iteratee, context) {
|
523
|
+
if (!_.isBoolean(isSorted)) {
|
524
|
+
context = iteratee;
|
525
|
+
iteratee = isSorted;
|
526
|
+
isSorted = false;
|
527
|
+
}
|
528
|
+
if (iteratee != null) iteratee = cb(iteratee, context);
|
529
|
+
var result = [];
|
530
|
+
var seen = [];
|
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);
|
381
544
|
}
|
382
|
-
|
383
|
-
|
384
|
-
return results;
|
545
|
+
}
|
546
|
+
return result;
|
385
547
|
};
|
386
548
|
|
387
549
|
// Produce an array that contains the union: each distinct element from all of
|
388
550
|
// the passed-in arrays.
|
389
551
|
_.union = function() {
|
390
|
-
return _.uniq(
|
552
|
+
return _.uniq(flatten(arguments, true, true));
|
391
553
|
};
|
392
554
|
|
393
555
|
// Produce an array that contains every item shared between all the
|
394
|
-
// passed-in arrays.
|
395
|
-
_.intersection =
|
396
|
-
var
|
397
|
-
|
398
|
-
|
399
|
-
|
400
|
-
|
401
|
-
|
556
|
+
// passed-in arrays.
|
557
|
+
_.intersection = function(array) {
|
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;
|
402
569
|
};
|
403
570
|
|
404
571
|
// Take the difference between one array and a number of other arrays.
|
405
572
|
// Only the elements present in just the first array will remain.
|
406
573
|
_.difference = function(array) {
|
407
|
-
var rest =
|
408
|
-
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
|
+
});
|
409
578
|
};
|
410
579
|
|
411
580
|
// Zip together multiple lists into a single array -- elements that share
|
412
581
|
// an index go together.
|
413
582
|
_.zip = function() {
|
414
|
-
|
415
|
-
var length = _.max(_.pluck(args, 'length'));
|
416
|
-
var results = new Array(length);
|
417
|
-
for (var i = 0; i < length; i++) results[i] = _.pluck(args, "" + i);
|
418
|
-
return results;
|
583
|
+
return _.unzip(arguments);
|
419
584
|
};
|
420
585
|
|
421
|
-
//
|
422
|
-
//
|
423
|
-
|
424
|
-
|
425
|
-
|
426
|
-
|
427
|
-
|
428
|
-
|
429
|
-
var i, l;
|
430
|
-
if (isSorted) {
|
431
|
-
i = _.sortedIndex(array, item);
|
432
|
-
return array[i] === item ? i : -1;
|
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);
|
433
594
|
}
|
434
|
-
|
435
|
-
for (i = 0, l = array.length; i < l; i++) if (i in array && array[i] === item) return i;
|
436
|
-
return -1;
|
595
|
+
return result;
|
437
596
|
};
|
438
597
|
|
439
|
-
//
|
440
|
-
|
441
|
-
|
442
|
-
|
443
|
-
var
|
444
|
-
|
445
|
-
|
598
|
+
// Converts lists into objects. Pass either a single array of `[key, value]`
|
599
|
+
// pairs, or two parallel arrays of the same length -- one of keys, and one of
|
600
|
+
// the corresponding values.
|
601
|
+
_.object = function(list, values) {
|
602
|
+
var result = {};
|
603
|
+
for (var i = 0, length = getLength(list); i < length; i++) {
|
604
|
+
if (values) {
|
605
|
+
result[list[i]] = values[i];
|
606
|
+
} else {
|
607
|
+
result[list[i][0]] = list[i][1];
|
608
|
+
}
|
609
|
+
}
|
610
|
+
return result;
|
446
611
|
};
|
447
612
|
|
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;
|
621
|
+
}
|
622
|
+
return -1;
|
623
|
+
};
|
624
|
+
}
|
625
|
+
|
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;
|
639
|
+
}
|
640
|
+
return low;
|
641
|
+
};
|
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
|
+
|
448
675
|
// Generate an integer Array containing an arithmetic progression. A port of
|
449
676
|
// the native Python `range()` function. See
|
450
677
|
// [the Python documentation](http://docs.python.org/library/functions.html#range).
|
451
678
|
_.range = function(start, stop, step) {
|
452
|
-
if (
|
679
|
+
if (stop == null) {
|
453
680
|
stop = start || 0;
|
454
681
|
start = 0;
|
455
682
|
}
|
456
|
-
step =
|
683
|
+
step = step || 1;
|
457
684
|
|
458
|
-
var
|
459
|
-
var
|
460
|
-
var range = new Array(len);
|
685
|
+
var length = Math.max(Math.ceil((stop - start) / step), 0);
|
686
|
+
var range = Array(length);
|
461
687
|
|
462
|
-
|
463
|
-
range[idx
|
464
|
-
start += step;
|
688
|
+
for (var idx = 0; idx < length; idx++, start += step) {
|
689
|
+
range[idx] = start;
|
465
690
|
}
|
466
691
|
|
467
692
|
return range;
|
@@ -470,80 +695,117 @@
|
|
470
695
|
// Function (ahem) Functions
|
471
696
|
// ------------------
|
472
697
|
|
473
|
-
//
|
474
|
-
|
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
|
+
};
|
475
707
|
|
476
708
|
// Create a function bound to a given object (assigning `this`, and arguments,
|
477
|
-
// optionally).
|
478
|
-
//
|
479
|
-
|
480
|
-
|
481
|
-
|
482
|
-
|
483
|
-
|
484
|
-
|
485
|
-
|
486
|
-
|
487
|
-
|
488
|
-
|
489
|
-
|
490
|
-
|
491
|
-
|
709
|
+
// optionally). Delegates to **ECMAScript 5**'s native `Function.bind` if
|
710
|
+
// available.
|
711
|
+
_.bind = function(func, context) {
|
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)));
|
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);
|
492
734
|
};
|
735
|
+
return bound;
|
493
736
|
};
|
494
737
|
|
495
|
-
// Bind
|
496
|
-
//
|
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.
|
497
741
|
_.bindAll = function(obj) {
|
498
|
-
var
|
499
|
-
if (
|
500
|
-
|
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
|
+
}
|
501
748
|
return obj;
|
502
749
|
};
|
503
750
|
|
504
751
|
// Memoize an expensive function by storing its results.
|
505
752
|
_.memoize = function(func, hasher) {
|
506
|
-
var
|
507
|
-
|
508
|
-
|
509
|
-
|
510
|
-
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];
|
511
758
|
};
|
759
|
+
memoize.cache = {};
|
760
|
+
return memoize;
|
512
761
|
};
|
513
762
|
|
514
763
|
// Delays a function for the given number of milliseconds, and then calls
|
515
764
|
// it with the arguments supplied.
|
516
765
|
_.delay = function(func, wait) {
|
517
766
|
var args = slice.call(arguments, 2);
|
518
|
-
return setTimeout(function(){
|
767
|
+
return setTimeout(function(){
|
768
|
+
return func.apply(null, args);
|
769
|
+
}, wait);
|
519
770
|
};
|
520
771
|
|
521
772
|
// Defers a function, scheduling it to run after the current call stack has
|
522
773
|
// cleared.
|
523
|
-
_.defer =
|
524
|
-
return _.delay.apply(_, [func, 1].concat(slice.call(arguments, 1)));
|
525
|
-
};
|
774
|
+
_.defer = _.partial(_.delay, _, 1);
|
526
775
|
|
527
776
|
// Returns a function, that, when invoked, will only be triggered at most once
|
528
|
-
// during a given window of time.
|
529
|
-
|
530
|
-
|
531
|
-
|
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;
|
784
|
+
var previous = 0;
|
785
|
+
if (!options) options = {};
|
786
|
+
var later = function() {
|
787
|
+
previous = options.leading === false ? 0 : _.now();
|
788
|
+
timeout = null;
|
789
|
+
result = func.apply(context, args);
|
790
|
+
if (!timeout) context = args = null;
|
791
|
+
};
|
532
792
|
return function() {
|
533
|
-
|
534
|
-
|
535
|
-
|
536
|
-
|
537
|
-
|
538
|
-
|
539
|
-
|
540
|
-
|
541
|
-
|
542
|
-
|
793
|
+
var now = _.now();
|
794
|
+
if (!previous && options.leading === false) previous = now;
|
795
|
+
var remaining = wait - (now - previous);
|
796
|
+
context = this;
|
797
|
+
args = arguments;
|
798
|
+
if (remaining <= 0 || remaining > wait) {
|
799
|
+
if (timeout) {
|
800
|
+
clearTimeout(timeout);
|
801
|
+
timeout = null;
|
802
|
+
}
|
803
|
+
previous = now;
|
543
804
|
result = func.apply(context, args);
|
805
|
+
if (!timeout) context = args = null;
|
806
|
+
} else if (!timeout && options.trailing !== false) {
|
807
|
+
timeout = setTimeout(later, remaining);
|
544
808
|
}
|
545
|
-
whenDone();
|
546
|
-
throttling = true;
|
547
809
|
return result;
|
548
810
|
};
|
549
811
|
};
|
@@ -553,27 +815,34 @@
|
|
553
815
|
// N milliseconds. If `immediate` is passed, trigger the function on the
|
554
816
|
// leading edge, instead of the trailing.
|
555
817
|
_.debounce = function(func, wait, immediate) {
|
556
|
-
var timeout;
|
557
|
-
|
558
|
-
|
559
|
-
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 {
|
560
826
|
timeout = null;
|
561
|
-
if (!immediate)
|
562
|
-
|
563
|
-
|
564
|
-
|
565
|
-
|
827
|
+
if (!immediate) {
|
828
|
+
result = func.apply(context, args);
|
829
|
+
if (!timeout) context = args = null;
|
830
|
+
}
|
831
|
+
}
|
566
832
|
};
|
567
|
-
};
|
568
833
|
|
569
|
-
// Returns a function that will be executed at most one time, no matter how
|
570
|
-
// often you call it. Useful for lazy initialization.
|
571
|
-
_.once = function(func) {
|
572
|
-
var ran = false, memo;
|
573
834
|
return function() {
|
574
|
-
|
575
|
-
|
576
|
-
|
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;
|
577
846
|
};
|
578
847
|
};
|
579
848
|
|
@@ -581,48 +850,146 @@
|
|
581
850
|
// allowing you to adjust arguments, run code before and after, and
|
582
851
|
// conditionally execute the original function.
|
583
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) {
|
584
858
|
return function() {
|
585
|
-
|
586
|
-
return wrapper.apply(this, args);
|
859
|
+
return !predicate.apply(this, arguments);
|
587
860
|
};
|
588
861
|
};
|
589
862
|
|
590
863
|
// Returns a function that is the composition of a list of functions, each
|
591
864
|
// consuming the return value of the function that follows.
|
592
865
|
_.compose = function() {
|
593
|
-
var
|
866
|
+
var args = arguments;
|
867
|
+
var start = args.length - 1;
|
594
868
|
return function() {
|
595
|
-
var
|
596
|
-
|
597
|
-
|
598
|
-
|
599
|
-
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;
|
600
873
|
};
|
601
874
|
};
|
602
875
|
|
603
|
-
// Returns a function that will only be executed after
|
876
|
+
// Returns a function that will only be executed on and after the Nth call.
|
604
877
|
_.after = function(times, func) {
|
605
|
-
if (times <= 0) return func();
|
606
878
|
return function() {
|
607
|
-
if (--times < 1) {
|
879
|
+
if (--times < 1) {
|
880
|
+
return func.apply(this, arguments);
|
881
|
+
}
|
882
|
+
};
|
883
|
+
};
|
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;
|
608
894
|
};
|
609
895
|
};
|
610
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
|
+
|
611
901
|
// Object Functions
|
612
902
|
// ----------------
|
613
903
|
|
614
|
-
//
|
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.
|
615
927
|
// Delegates to **ECMAScript 5**'s native `Object.keys`
|
616
|
-
_.keys =
|
617
|
-
if (
|
928
|
+
_.keys = function(obj) {
|
929
|
+
if (!_.isObject(obj)) return [];
|
930
|
+
if (nativeKeys) return nativeKeys(obj);
|
931
|
+
var 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 [];
|
618
941
|
var keys = [];
|
619
|
-
for (var key in obj)
|
942
|
+
for (var key in obj) keys.push(key);
|
943
|
+
// Ahem, IE < 9.
|
944
|
+
if (hasEnumBug) collectNonEnumProps(obj, keys);
|
620
945
|
return keys;
|
621
946
|
};
|
622
947
|
|
623
948
|
// Retrieve the values of an object's properties.
|
624
949
|
_.values = function(obj) {
|
625
|
-
|
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
|
+
}
|
956
|
+
return values;
|
957
|
+
};
|
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
|
+
|
974
|
+
// Convert an object into a list of `[key, value]` pairs.
|
975
|
+
_.pairs = function(obj) {
|
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
|
+
}
|
982
|
+
return pairs;
|
983
|
+
};
|
984
|
+
|
985
|
+
// Invert the keys and values of an object. The values must be serializable.
|
986
|
+
_.invert = function(obj) {
|
987
|
+
var result = {};
|
988
|
+
var keys = _.keys(obj);
|
989
|
+
for (var i = 0, length = keys.length; i < length; i++) {
|
990
|
+
result[obj[keys[i]]] = keys[i];
|
991
|
+
}
|
992
|
+
return result;
|
626
993
|
};
|
627
994
|
|
628
995
|
// Return a sorted list of the function names available on the object.
|
@@ -636,32 +1003,65 @@
|
|
636
1003
|
};
|
637
1004
|
|
638
1005
|
// Extend a given object with all the properties in passed-in object(s).
|
639
|
-
_.extend =
|
640
|
-
|
641
|
-
|
642
|
-
|
643
|
-
|
644
|
-
|
645
|
-
|
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
|
+
}
|
646
1020
|
};
|
647
1021
|
|
648
1022
|
// Return a copy of the object only containing the whitelisted properties.
|
649
|
-
_.pick = function(
|
650
|
-
var result = {};
|
651
|
-
|
652
|
-
|
653
|
-
|
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
|
+
}
|
654
1039
|
return result;
|
655
1040
|
};
|
656
1041
|
|
1042
|
+
// Return a copy of the object without the blacklisted properties.
|
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
|
+
};
|
1051
|
+
}
|
1052
|
+
return _.pick(obj, iteratee, context);
|
1053
|
+
};
|
1054
|
+
|
657
1055
|
// Fill in a given object with default properties.
|
658
|
-
_.defaults =
|
659
|
-
|
660
|
-
|
661
|
-
|
662
|
-
|
663
|
-
|
664
|
-
|
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;
|
665
1065
|
};
|
666
1066
|
|
667
1067
|
// Create a (shallow-cloned) duplicate of an object.
|
@@ -678,173 +1078,178 @@
|
|
678
1078
|
return obj;
|
679
1079
|
};
|
680
1080
|
|
681
|
-
//
|
682
|
-
function
|
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
|
+
|
1094
|
+
// Internal recursive comparison function for `isEqual`.
|
1095
|
+
var eq = function(a, b, aStack, bStack) {
|
683
1096
|
// Identical objects are equal. `0 === -0`, but they aren't identical.
|
684
|
-
// See the Harmony `egal` proposal
|
685
|
-
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;
|
686
1099
|
// A strict comparison is necessary because `null == undefined`.
|
687
1100
|
if (a == null || b == null) return a === b;
|
688
1101
|
// 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);
|
1102
|
+
if (a instanceof _) a = a._wrapped;
|
1103
|
+
if (b instanceof _) b = b._wrapped;
|
694
1104
|
// Compare `[[Class]]` names.
|
695
1105
|
var className = toString.call(a);
|
696
|
-
if (className
|
1106
|
+
if (className !== toString.call(b)) return false;
|
697
1107
|
switch (className) {
|
698
|
-
// 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')
|
699
1111
|
case '[object String]':
|
700
1112
|
// Primitives and their corresponding object wrappers are equivalent; thus, `"5"` is
|
701
1113
|
// equivalent to `new String("5")`.
|
702
|
-
return a
|
1114
|
+
return '' + a === '' + b;
|
703
1115
|
case '[object Number]':
|
704
|
-
// `NaN`s are equivalent, but non-reflexive.
|
705
|
-
//
|
706
|
-
|
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;
|
707
1121
|
case '[object Date]':
|
708
1122
|
case '[object Boolean]':
|
709
1123
|
// Coerce dates and booleans to numeric primitive values. Dates are compared by their
|
710
1124
|
// millisecond representations. Note that invalid dates with millisecond representations
|
711
1125
|
// of `NaN` are not equivalent.
|
712
|
-
return +a
|
713
|
-
|
714
|
-
|
715
|
-
|
716
|
-
|
717
|
-
|
718
|
-
|
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
|
+
}
|
719
1141
|
}
|
720
|
-
if (typeof a != 'object' || typeof b != 'object') return false;
|
721
1142
|
// Assume equality for cyclic structures. The algorithm for detecting cyclic
|
722
1143
|
// structures is adapted from ES 5.1 section 15.12.3, abstract operation `JO`.
|
723
|
-
|
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 || [];
|
1149
|
+
var length = aStack.length;
|
724
1150
|
while (length--) {
|
725
1151
|
// Linear search. Performance is inversely proportional to the number of
|
726
1152
|
// unique nested structures.
|
727
|
-
if (
|
1153
|
+
if (aStack[length] === a) return bStack[length] === b;
|
728
1154
|
}
|
1155
|
+
|
729
1156
|
// Add the first object to the stack of traversed objects.
|
730
|
-
|
731
|
-
|
1157
|
+
aStack.push(a);
|
1158
|
+
bStack.push(b);
|
1159
|
+
|
732
1160
|
// Recursively compare objects and arrays.
|
733
|
-
if (
|
1161
|
+
if (areArrays) {
|
734
1162
|
// Compare array lengths to determine if a deep comparison is necessary.
|
735
|
-
|
736
|
-
|
737
|
-
|
738
|
-
|
739
|
-
|
740
|
-
// Ensure commutative equality for sparse arrays.
|
741
|
-
if (!(result = size in a == size in b && eq(a[size], b[size], stack))) break;
|
742
|
-
}
|
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;
|
743
1168
|
}
|
744
1169
|
} else {
|
745
|
-
// Objects with different constructors are not equivalent.
|
746
|
-
if ('constructor' in a != 'constructor' in b || a.constructor != b.constructor) return false;
|
747
1170
|
// Deep compare objects.
|
748
|
-
|
749
|
-
|
750
|
-
|
751
|
-
|
752
|
-
|
753
|
-
|
754
|
-
|
755
|
-
|
756
|
-
// Ensure that both objects contain the same number of properties.
|
757
|
-
if (result) {
|
758
|
-
for (key in b) {
|
759
|
-
if (_.has(b, key) && !(size--)) break;
|
760
|
-
}
|
761
|
-
result = !size;
|
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;
|
762
1179
|
}
|
763
1180
|
}
|
764
1181
|
// Remove the first object from the stack of traversed objects.
|
765
|
-
|
766
|
-
|
767
|
-
|
1182
|
+
aStack.pop();
|
1183
|
+
bStack.pop();
|
1184
|
+
return true;
|
1185
|
+
};
|
768
1186
|
|
769
1187
|
// Perform a deep comparison to check if two objects are equal.
|
770
1188
|
_.isEqual = function(a, b) {
|
771
|
-
return eq(a, b
|
1189
|
+
return eq(a, b);
|
772
1190
|
};
|
773
1191
|
|
774
1192
|
// Is a given array, string, or object empty?
|
775
1193
|
// An "empty" object has no enumerable own-properties.
|
776
1194
|
_.isEmpty = function(obj) {
|
777
1195
|
if (obj == null) return true;
|
778
|
-
if (_.isArray(obj) || _.isString(obj)) return obj.length === 0;
|
779
|
-
|
780
|
-
return true;
|
1196
|
+
if (isArrayLike(obj) && (_.isArray(obj) || _.isString(obj) || _.isArguments(obj))) return obj.length === 0;
|
1197
|
+
return _.keys(obj).length === 0;
|
781
1198
|
};
|
782
1199
|
|
783
1200
|
// Is a given value a DOM element?
|
784
1201
|
_.isElement = function(obj) {
|
785
|
-
return !!(obj && obj.nodeType
|
1202
|
+
return !!(obj && obj.nodeType === 1);
|
786
1203
|
};
|
787
1204
|
|
788
1205
|
// Is a given value an array?
|
789
1206
|
// Delegates to ECMA5's native Array.isArray
|
790
1207
|
_.isArray = nativeIsArray || function(obj) {
|
791
|
-
return toString.call(obj)
|
1208
|
+
return toString.call(obj) === '[object Array]';
|
792
1209
|
};
|
793
1210
|
|
794
1211
|
// Is a given variable an object?
|
795
1212
|
_.isObject = function(obj) {
|
796
|
-
|
1213
|
+
var type = typeof obj;
|
1214
|
+
return type === 'function' || type === 'object' && !!obj;
|
797
1215
|
};
|
798
1216
|
|
799
|
-
//
|
800
|
-
_.
|
801
|
-
|
802
|
-
|
1217
|
+
// Add some isType methods: isArguments, isFunction, isString, isNumber, isDate, isRegExp, isError.
|
1218
|
+
_.each(['Arguments', 'Function', 'String', 'Number', 'Date', 'RegExp', 'Error'], function(name) {
|
1219
|
+
_['is' + name] = function(obj) {
|
1220
|
+
return toString.call(obj) === '[object ' + name + ']';
|
1221
|
+
};
|
1222
|
+
});
|
1223
|
+
|
1224
|
+
// Define a fallback version of the method in browsers (ahem, IE < 9), where
|
1225
|
+
// there isn't any inspectable "Arguments" type.
|
803
1226
|
if (!_.isArguments(arguments)) {
|
804
1227
|
_.isArguments = function(obj) {
|
805
|
-
return
|
1228
|
+
return _.has(obj, 'callee');
|
806
1229
|
};
|
807
1230
|
}
|
808
1231
|
|
809
|
-
//
|
810
|
-
|
811
|
-
|
812
|
-
|
813
|
-
|
814
|
-
|
815
|
-
|
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
|
-
};
|
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') {
|
1235
|
+
_.isFunction = function(obj) {
|
1236
|
+
return typeof obj == 'function' || false;
|
1237
|
+
};
|
1238
|
+
}
|
823
1239
|
|
824
1240
|
// Is a given object a finite number?
|
825
1241
|
_.isFinite = function(obj) {
|
826
|
-
return
|
1242
|
+
return isFinite(obj) && !isNaN(parseFloat(obj));
|
827
1243
|
};
|
828
1244
|
|
829
|
-
// Is the given value `NaN`?
|
1245
|
+
// Is the given value `NaN`? (NaN is the only number which does not equal itself).
|
830
1246
|
_.isNaN = function(obj) {
|
831
|
-
|
832
|
-
return obj !== obj;
|
1247
|
+
return _.isNumber(obj) && obj !== +obj;
|
833
1248
|
};
|
834
1249
|
|
835
1250
|
// Is a given value a boolean?
|
836
1251
|
_.isBoolean = function(obj) {
|
837
|
-
return obj === true || obj === false || toString.call(obj)
|
838
|
-
};
|
839
|
-
|
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]';
|
1252
|
+
return obj === true || obj === false || toString.call(obj) === '[object Boolean]';
|
848
1253
|
};
|
849
1254
|
|
850
1255
|
// Is a given value equal to null?
|
@@ -857,9 +1262,10 @@
|
|
857
1262
|
return obj === void 0;
|
858
1263
|
};
|
859
1264
|
|
860
|
-
//
|
1265
|
+
// Shortcut function for checking if an object has a given property directly
|
1266
|
+
// on itself (in other words, not on a prototype).
|
861
1267
|
_.has = function(obj, key) {
|
862
|
-
return hasOwnProperty.call(obj, key);
|
1268
|
+
return obj != null && hasOwnProperty.call(obj, key);
|
863
1269
|
};
|
864
1270
|
|
865
1271
|
// Utility Functions
|
@@ -872,42 +1278,103 @@
|
|
872
1278
|
return this;
|
873
1279
|
};
|
874
1280
|
|
875
|
-
// Keep the identity function around for default
|
1281
|
+
// Keep the identity function around for default iteratees.
|
876
1282
|
_.identity = function(value) {
|
877
1283
|
return value;
|
878
1284
|
};
|
879
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
|
+
|
880
1313
|
// Run a function **n** times.
|
881
|
-
_.times = function
|
882
|
-
|
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);
|
1318
|
+
return accum;
|
1319
|
+
};
|
1320
|
+
|
1321
|
+
// Return a random integer between min and max (inclusive).
|
1322
|
+
_.random = function(min, max) {
|
1323
|
+
if (max == null) {
|
1324
|
+
max = min;
|
1325
|
+
min = 0;
|
1326
|
+
}
|
1327
|
+
return min + Math.floor(Math.random() * (max - min + 1));
|
883
1328
|
};
|
884
1329
|
|
885
|
-
//
|
886
|
-
_.
|
887
|
-
return
|
1330
|
+
// A (possibly faster) way to get the current timestamp as an integer.
|
1331
|
+
_.now = Date.now || function() {
|
1332
|
+
return new Date().getTime();
|
888
1333
|
};
|
889
1334
|
|
890
|
-
|
891
|
-
|
892
|
-
|
893
|
-
|
894
|
-
|
895
|
-
|
1335
|
+
// List of HTML entities for escaping.
|
1336
|
+
var escapeMap = {
|
1337
|
+
'&': '&',
|
1338
|
+
'<': '<',
|
1339
|
+
'>': '>',
|
1340
|
+
'"': '"',
|
1341
|
+
"'": ''',
|
1342
|
+
'`': '`'
|
896
1343
|
};
|
1344
|
+
var unescapeMap = _.invert(escapeMap);
|
897
1345
|
|
898
|
-
//
|
899
|
-
|
900
|
-
|
901
|
-
|
902
|
-
|
903
|
-
|
1346
|
+
// Functions for escaping and unescaping strings to/from HTML interpolation.
|
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;
|
1358
|
+
};
|
1359
|
+
};
|
1360
|
+
_.escape = createEscaper(escapeMap);
|
1361
|
+
_.unescape = createEscaper(unescapeMap);
|
1362
|
+
|
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;
|
904
1371
|
};
|
905
1372
|
|
906
1373
|
// Generate a unique integer id (unique within the entire client session).
|
907
1374
|
// Useful for temporary DOM ids.
|
908
1375
|
var idCounter = 0;
|
909
1376
|
_.uniqueId = function(prefix) {
|
910
|
-
var id = idCounter
|
1377
|
+
var id = ++idCounter + '';
|
911
1378
|
return prefix ? prefix + id : id;
|
912
1379
|
};
|
913
1380
|
|
@@ -922,138 +1389,160 @@
|
|
922
1389
|
// When customizing `templateSettings`, if you don't want to define an
|
923
1390
|
// interpolation, evaluation or escaping regex, we need one that is
|
924
1391
|
// guaranteed not to match.
|
925
|
-
var noMatch =
|
1392
|
+
var noMatch = /(.)^/;
|
926
1393
|
|
927
1394
|
// Certain characters need to be escaped so that they can be put into a
|
928
1395
|
// string literal.
|
929
1396
|
var escapes = {
|
930
|
-
'
|
931
|
-
|
932
|
-
'r':
|
933
|
-
'n':
|
934
|
-
'
|
935
|
-
'
|
936
|
-
|
937
|
-
|
938
|
-
|
939
|
-
|
940
|
-
var
|
941
|
-
|
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
|
-
});
|
1397
|
+
"'": "'",
|
1398
|
+
'\\': '\\',
|
1399
|
+
'\r': 'r',
|
1400
|
+
'\n': 'n',
|
1401
|
+
'\u2028': 'u2028',
|
1402
|
+
'\u2029': 'u2029'
|
1403
|
+
};
|
1404
|
+
|
1405
|
+
var escaper = /\\|'|\r|\n|\u2028|\u2029/g;
|
1406
|
+
|
1407
|
+
var escapeChar = function(match) {
|
1408
|
+
return '\\' + escapes[match];
|
949
1409
|
};
|
950
1410
|
|
951
1411
|
// JavaScript micro-templating, similar to John Resig's implementation.
|
952
1412
|
// Underscore templating handles arbitrary delimiters, preserves whitespace,
|
953
1413
|
// and correctly escapes quotes within interpolated code.
|
954
|
-
|
955
|
-
|
956
|
-
|
957
|
-
|
958
|
-
|
959
|
-
//
|
960
|
-
var
|
961
|
-
.
|
962
|
-
|
963
|
-
|
964
|
-
|
965
|
-
|
966
|
-
|
967
|
-
|
968
|
-
|
969
|
-
|
970
|
-
.
|
971
|
-
|
972
|
-
|
1414
|
+
// NB: `oldSettings` only exists for backwards compatibility.
|
1415
|
+
_.template = function(text, settings, oldSettings) {
|
1416
|
+
if (!settings && oldSettings) settings = oldSettings;
|
1417
|
+
settings = _.defaults({}, settings, _.templateSettings);
|
1418
|
+
|
1419
|
+
// Combine delimiters into one regular expression via alternation.
|
1420
|
+
var matcher = RegExp([
|
1421
|
+
(settings.escape || noMatch).source,
|
1422
|
+
(settings.interpolate || noMatch).source,
|
1423
|
+
(settings.evaluate || noMatch).source
|
1424
|
+
].join('|') + '|$', 'g');
|
1425
|
+
|
1426
|
+
// Compile the template source, escaping string literals appropriately.
|
1427
|
+
var index = 0;
|
1428
|
+
var source = "__p+='";
|
1429
|
+
text.replace(matcher, function(match, escape, interpolate, evaluate, offset) {
|
1430
|
+
source += text.slice(index, offset).replace(escaper, escapeChar);
|
1431
|
+
index = offset + match.length;
|
1432
|
+
|
1433
|
+
if (escape) {
|
1434
|
+
source += "'+\n((__t=(" + escape + "))==null?'':_.escape(__t))+\n'";
|
1435
|
+
} else if (interpolate) {
|
1436
|
+
source += "'+\n((__t=(" + interpolate + "))==null?'':__t)+\n'";
|
1437
|
+
} else if (evaluate) {
|
1438
|
+
source += "';\n" + evaluate + "\n__p+='";
|
1439
|
+
}
|
1440
|
+
|
1441
|
+
// Adobe VMs need the match returned to produce the correct offest.
|
1442
|
+
return match;
|
1443
|
+
});
|
1444
|
+
source += "';\n";
|
973
1445
|
|
974
1446
|
// If a variable is not specified, place data values in local scope.
|
975
1447
|
if (!settings.variable) source = 'with(obj||{}){\n' + source + '}\n';
|
976
1448
|
|
977
|
-
source = "var __p=''
|
978
|
-
"
|
979
|
-
source +
|
1449
|
+
source = "var __t,__p='',__j=Array.prototype.join," +
|
1450
|
+
"print=function(){__p+=__j.call(arguments,'');};\n" +
|
1451
|
+
source + 'return __p;\n';
|
1452
|
+
|
1453
|
+
try {
|
1454
|
+
var render = new Function(settings.variable || 'obj', '_', source);
|
1455
|
+
} catch (e) {
|
1456
|
+
e.source = source;
|
1457
|
+
throw e;
|
1458
|
+
}
|
980
1459
|
|
981
|
-
var render = new Function(settings.variable || 'obj', '_', source);
|
982
|
-
if (data) return render(data, _);
|
983
1460
|
var template = function(data) {
|
984
1461
|
return render.call(this, data, _);
|
985
1462
|
};
|
986
1463
|
|
987
|
-
// Provide the compiled
|
988
|
-
|
989
|
-
template.source = 'function(' +
|
990
|
-
source + '}';
|
1464
|
+
// Provide the compiled source as a convenience for precompilation.
|
1465
|
+
var argument = settings.variable || 'obj';
|
1466
|
+
template.source = 'function(' + argument + '){\n' + source + '}';
|
991
1467
|
|
992
1468
|
return template;
|
993
1469
|
};
|
994
1470
|
|
995
|
-
// Add a "chain" function
|
1471
|
+
// Add a "chain" function. Start chaining a wrapped Underscore object.
|
996
1472
|
_.chain = function(obj) {
|
997
|
-
|
1473
|
+
var instance = _(obj);
|
1474
|
+
instance._chain = true;
|
1475
|
+
return instance;
|
998
1476
|
};
|
999
1477
|
|
1000
|
-
//
|
1478
|
+
// OOP
|
1001
1479
|
// ---------------
|
1002
|
-
|
1003
1480
|
// If Underscore is called as a function, it returns a wrapped object that
|
1004
1481
|
// can be used OO-style. This wrapper holds altered versions of all the
|
1005
1482
|
// 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
1483
|
|
1011
1484
|
// Helper function to continue chaining intermediate results.
|
1012
|
-
var result = function(
|
1013
|
-
return
|
1485
|
+
var result = function(instance, obj) {
|
1486
|
+
return instance._chain ? _(obj).chain() : obj;
|
1014
1487
|
};
|
1015
1488
|
|
1016
|
-
//
|
1017
|
-
|
1018
|
-
|
1019
|
-
var
|
1020
|
-
|
1021
|
-
|
1022
|
-
|
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
|
+
});
|
1023
1499
|
};
|
1024
1500
|
|
1025
1501
|
// Add all of the Underscore functions to the wrapper object.
|
1026
1502
|
_.mixin(_);
|
1027
1503
|
|
1028
1504
|
// Add all mutator Array functions to the wrapper.
|
1029
|
-
each(['pop', 'push', 'reverse', 'shift', 'sort', 'splice', 'unshift'], function(name) {
|
1505
|
+
_.each(['pop', 'push', 'reverse', 'shift', 'sort', 'splice', 'unshift'], function(name) {
|
1030
1506
|
var method = ArrayProto[name];
|
1031
|
-
|
1032
|
-
var
|
1033
|
-
method.apply(
|
1034
|
-
|
1035
|
-
|
1036
|
-
return result(wrapped, this._chain);
|
1507
|
+
_.prototype[name] = function() {
|
1508
|
+
var obj = this._wrapped;
|
1509
|
+
method.apply(obj, arguments);
|
1510
|
+
if ((name === 'shift' || name === 'splice') && obj.length === 0) delete obj[0];
|
1511
|
+
return result(this, obj);
|
1037
1512
|
};
|
1038
1513
|
});
|
1039
1514
|
|
1040
1515
|
// Add all accessor Array functions to the wrapper.
|
1041
|
-
each(['concat', 'join', 'slice'], function(name) {
|
1516
|
+
_.each(['concat', 'join', 'slice'], function(name) {
|
1042
1517
|
var method = ArrayProto[name];
|
1043
|
-
|
1044
|
-
return result(method.apply(this._wrapped, arguments)
|
1518
|
+
_.prototype[name] = function() {
|
1519
|
+
return result(this, method.apply(this._wrapped, arguments));
|
1045
1520
|
};
|
1046
1521
|
});
|
1047
1522
|
|
1048
|
-
// Start chaining a wrapped Underscore object.
|
1049
|
-
wrapper.prototype.chain = function() {
|
1050
|
-
this._chain = true;
|
1051
|
-
return this;
|
1052
|
-
};
|
1053
|
-
|
1054
1523
|
// Extracts the result from a wrapped and chained object.
|
1055
|
-
|
1524
|
+
_.prototype.value = function() {
|
1056
1525
|
return this._wrapped;
|
1057
1526
|
};
|
1058
1527
|
|
1059
|
-
|
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;
|
1531
|
+
|
1532
|
+
_.prototype.toString = function() {
|
1533
|
+
return '' + this._wrapped;
|
1534
|
+
};
|
1535
|
+
|
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));
|