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