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