underscore-source 0.0.2 → 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
data/Rakefile CHANGED
@@ -1,2 +1,88 @@
1
1
  #!/usr/bin/env rake
2
2
  require "bundler/gem_tasks"
3
+
4
+ def download(version)
5
+ source_url = "https://raw.github.com/documentcloud/underscore/#{version}/underscore.js"
6
+
7
+ `wget -O vendor/assets/javascripts/underscore.js #{source_url}`
8
+ end
9
+
10
+ def write_version_file(version)
11
+ version_file_text =
12
+ <<-eos.gsub(/^ {4}/, '')
13
+ module Underscore
14
+ module Source
15
+ VERSION = "#{version}"
16
+ end
17
+ end
18
+ eos
19
+
20
+ File.open('lib/underscore-source/version.rb', 'w') do |file|
21
+ file.puts version_file_text
22
+ end
23
+ end
24
+
25
+ def make_version(version)
26
+ download(version)
27
+ write_version_file(version)
28
+ `git ci -am "Underscore #{version}"`
29
+ puts `rake release`
30
+ end
31
+
32
+ task :make_all_versions do
33
+ versions = %w[
34
+ 0.1.0
35
+ 0.1.1
36
+ 0.2.0
37
+ 0.3.0
38
+ 0.3.1
39
+ 0.3.2
40
+ 0.3.3
41
+ 0.4.0
42
+ 0.4.1
43
+ 0.4.2
44
+ 0.4.3
45
+ 0.4.4
46
+ 0.4.5
47
+ 0.4.6
48
+ 0.4.7
49
+ 0.5.0
50
+ 0.5.1
51
+ 0.5.2
52
+ 0.5.3
53
+ 0.5.4
54
+ 0.5.5
55
+ 0.5.7
56
+ 0.6.0
57
+ 1.0.0
58
+ 1.0.1
59
+ 1.0.2
60
+ 1.0.3
61
+ 1.0.4
62
+ 1.1.0
63
+ 1.1.1
64
+ 1.1.2
65
+ 1.1.3
66
+ 1.1.4
67
+ 1.1.5
68
+ 1.1.6
69
+ 1.1.7
70
+ 1.2.0
71
+ 1.2.1
72
+ 1.2.2
73
+ 1.2.3
74
+ 1.2.4
75
+ 1.3.0
76
+ 1.3.1
77
+ 1.3.2
78
+ 1.3.3
79
+ ]
80
+
81
+ versions.each do |version|
82
+ make_version(version)
83
+ end
84
+ end
85
+
86
+ task :make_version do
87
+ make_version("0.1.0")
88
+ end
@@ -1,5 +1,5 @@
1
1
  module Underscore
2
2
  module Source
3
- VERSION = "0.0.2"
3
+ VERSION = "0.1.0"
4
4
  end
5
5
  end
@@ -1,1059 +1,415 @@
1
- // Underscore.js 1.3.3
2
- // (c) 2009-2012 Jeremy Ashkenas, DocumentCloud Inc.
3
- // Underscore is freely distributable under the MIT license.
4
- // Portions of Underscore are inspired or borrowed from Prototype,
5
- // Oliver Steele's Functional, and John Resig's Micro-Templating.
6
- // For all details and documentation:
7
- // http://documentcloud.github.com/underscore
8
-
9
- (function() {
10
-
11
- // Baseline setup
12
- // --------------
13
-
14
- // Establish the root object, `window` in the browser, or `global` on the server.
15
- var root = this;
16
-
17
- // Save the previous value of the `_` variable.
18
- var previousUnderscore = root._;
19
-
20
- // Establish the object that gets returned to break out of a loop iteration.
21
- var breaker = {};
22
-
23
- // Save bytes in the minified (but not gzipped) version:
24
- var ArrayProto = Array.prototype, ObjProto = Object.prototype, FuncProto = Function.prototype;
25
-
26
- // Create quick reference variables for speed access to core prototypes.
27
- var slice = ArrayProto.slice,
28
- unshift = ArrayProto.unshift,
29
- toString = ObjProto.toString,
30
- hasOwnProperty = ObjProto.hasOwnProperty;
31
-
32
- // All **ECMAScript 5** native function implementations that we hope to use
33
- // are declared here.
34
- var
35
- nativeForEach = ArrayProto.forEach,
36
- nativeMap = ArrayProto.map,
37
- nativeReduce = ArrayProto.reduce,
38
- nativeReduceRight = ArrayProto.reduceRight,
39
- nativeFilter = ArrayProto.filter,
40
- nativeEvery = ArrayProto.every,
41
- nativeSome = ArrayProto.some,
42
- nativeIndexOf = ArrayProto.indexOf,
43
- nativeLastIndexOf = ArrayProto.lastIndexOf,
44
- nativeIsArray = Array.isArray,
45
- nativeKeys = Object.keys,
46
- nativeBind = FuncProto.bind;
47
-
48
- // Create a safe reference to the Underscore object for use below.
49
- var _ = function(obj) { return new wrapper(obj); };
50
-
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._ = _;
60
- } else {
61
- root['_'] = _;
62
- }
63
-
64
- // Current version.
65
- _.VERSION = '1.3.3';
66
-
67
- // Collection Functions
68
- // --------------------
69
-
70
- // The cornerstone, an `each` implementation, aka `forEach`.
71
- // Handles objects with the built-in `forEach`, arrays, and raw objects.
72
- // Delegates to **ECMAScript 5**'s native `forEach` if available.
73
- var each = _.each = _.forEach = function(obj, iterator, context) {
74
- if (obj == null) return;
75
- if (nativeForEach && obj.forEach === nativeForEach) {
76
- obj.forEach(iterator, context);
77
- } else if (obj.length === +obj.length) {
78
- for (var i = 0, l = obj.length; i < l; i++) {
79
- if (i in obj && iterator.call(context, obj[i], i, obj) === breaker) return;
80
- }
81
- } else {
82
- for (var key in obj) {
83
- if (_.has(obj, key)) {
84
- if (iterator.call(context, obj[key], key, obj) === breaker) return;
1
+ // Underscore.js
2
+ // (c) 2009 Jeremy Ashkenas, DocumentCloud Inc.
3
+ // Underscore is freely distributable under the terms of the MIT license.
4
+ // Portions of Underscore are inspired by or borrowed from Prototype.js,
5
+ // Oliver Steele's Functional, And John Resig's Micro-Templating.
6
+ // For all details and documentation:
7
+ // http://documentcloud.github.com/underscore/
8
+ window._ = {
9
+
10
+ VERSION : '0.1.0',
11
+
12
+ /*------------------------ Collection Functions: ---------------------------*/
13
+
14
+ // The cornerstone, an each implementation.
15
+ // Handles objects implementing forEach, each, arrays, and raw objects.
16
+ each : function(obj, iterator, context) {
17
+ var index = 0;
18
+ try {
19
+ if (obj.forEach) {
20
+ obj.forEach(iterator, context);
21
+ } else if (obj.length) {
22
+ for (var i=0; i<obj.length; i++) iterator.call(context, obj[i], i);
23
+ } else if (obj.each) {
24
+ obj.each(function(value) { iterator.call(context, value, index++); });
25
+ } else {
26
+ var i = 0;
27
+ for (var key in obj) {
28
+ var value = obj[key], pair = [key, value];
29
+ pair.key = key;
30
+ pair.value = value;
31
+ iterator.call(context, pair, i++);
85
32
  }
86
33
  }
34
+ } catch(e) {
35
+ if (e != '__break__') throw e;
87
36
  }
88
- };
89
-
90
- // Return the results of applying the iterator to each element.
91
- // Delegates to **ECMAScript 5**'s native `map` if available.
92
- _.map = _.collect = function(obj, iterator, context) {
37
+ return obj;
38
+ },
39
+
40
+ // Return the results of applying the iterator to each element. Use Javascript
41
+ // 1.6's version of map, if possible.
42
+ map : function(obj, iterator, context) {
43
+ if (obj && obj.map) return obj.map(iterator, context);
93
44
  var results = [];
94
- if (obj == null) return results;
95
- if (nativeMap && obj.map === nativeMap) return obj.map(iterator, context);
96
- each(obj, function(value, index, list) {
97
- results[results.length] = iterator.call(context, value, index, list);
45
+ _.each(obj, function(value, index) {
46
+ results.push(iterator.call(context, value, index));
98
47
  });
99
- if (obj.length === +obj.length) results.length = obj.length;
100
48
  return results;
101
- };
102
-
103
- // **Reduce** builds up a single result from a list of values, aka `inject`,
104
- // or `foldl`. Delegates to **ECMAScript 5**'s native `reduce` if available.
105
- _.reduce = _.foldl = _.inject = function(obj, iterator, memo, context) {
106
- var initial = arguments.length > 2;
107
- if (obj == null) obj = [];
108
- if (nativeReduce && obj.reduce === nativeReduce) {
109
- if (context) iterator = _.bind(iterator, context);
110
- return initial ? obj.reduce(iterator, memo) : obj.reduce(iterator);
111
- }
112
- each(obj, function(value, index, list) {
113
- if (!initial) {
114
- memo = value;
115
- initial = true;
116
- } else {
117
- memo = iterator.call(context, memo, value, index, list);
118
- }
49
+ },
50
+
51
+ // Inject builds up a single result from a list of values. Also known as
52
+ // reduce, or foldl.
53
+ inject : function(obj, memo, iterator, context) {
54
+ _.each(obj, function(value, index) {
55
+ memo = iterator.call(context, memo, value, index);
119
56
  });
120
- if (!initial) throw new TypeError('Reduce of empty array with no initial value');
121
57
  return memo;
122
- };
123
-
124
- // The right-associative version of reduce, also known as `foldr`.
125
- // Delegates to **ECMAScript 5**'s native `reduceRight` if available.
126
- _.reduceRight = _.foldr = function(obj, iterator, memo, context) {
127
- var initial = arguments.length > 2;
128
- if (obj == null) obj = [];
129
- if (nativeReduceRight && obj.reduceRight === nativeReduceRight) {
130
- if (context) iterator = _.bind(iterator, context);
131
- return initial ? obj.reduceRight(iterator, memo) : obj.reduceRight(iterator);
132
- }
133
- var reversed = _.toArray(obj).reverse();
134
- if (context && !initial) iterator = _.bind(iterator, context);
135
- return initial ? _.reduce(reversed, iterator, memo, context) : _.reduce(reversed, iterator);
136
- };
137
-
138
- // Return the first value which passes a truth test. Aliased as `detect`.
139
- _.find = _.detect = function(obj, iterator, context) {
58
+ },
59
+
60
+ // Return the first value which passes a truth test.
61
+ detect : function(obj, iterator, context) {
140
62
  var result;
141
- any(obj, function(value, index, list) {
142
- if (iterator.call(context, value, index, list)) {
63
+ _.each(obj, function(value, index) {
64
+ if (iterator.call(context, value, index)) {
143
65
  result = value;
144
- return true;
66
+ throw '__break__';
145
67
  }
146
68
  });
147
69
  return result;
148
- };
149
-
150
- // Return all the elements that pass a truth test.
151
- // Delegates to **ECMAScript 5**'s native `filter` if available.
152
- // Aliased as `select`.
153
- _.filter = _.select = function(obj, iterator, context) {
70
+ },
71
+
72
+ // Return all the elements that pass a truth test. Use Javascript 1.6's
73
+ // filter(), if it exists.
74
+ select : function(obj, iterator, context) {
75
+ if (obj.filter) return obj.filter(iterator, context);
154
76
  var results = [];
155
- if (obj == null) return results;
156
- if (nativeFilter && obj.filter === nativeFilter) return obj.filter(iterator, context);
157
- each(obj, function(value, index, list) {
158
- if (iterator.call(context, value, index, list)) results[results.length] = value;
77
+ _.each(obj, function(value, index) {
78
+ if (iterator.call(context, value, index)) results.push(value);
159
79
  });
160
80
  return results;
161
- };
162
-
81
+ },
82
+
163
83
  // Return all the elements for which a truth test fails.
164
- _.reject = function(obj, iterator, context) {
84
+ reject : function(obj, iterator, context) {
165
85
  var results = [];
166
- if (obj == null) return results;
167
- each(obj, function(value, index, list) {
168
- if (!iterator.call(context, value, index, list)) results[results.length] = value;
86
+ _.each(obj, function(value, index) {
87
+ if (!iterator.call(context, value, index)) results.push(value);
169
88
  });
170
89
  return results;
171
- };
172
-
173
- // Determine whether all of the elements match a truth test.
174
- // Delegates to **ECMAScript 5**'s native `every` if available.
175
- // Aliased as `all`.
176
- _.every = _.all = function(obj, iterator, context) {
90
+ },
91
+
92
+ // Determine whether all of the elements match a truth test. Delegate to
93
+ // Javascript 1.6's every(), if it is present.
94
+ all : function(obj, iterator, context) {
95
+ iterator = iterator || function(v){ return v; };
96
+ if (obj.every) return obj.every(iterator, context);
177
97
  var result = true;
178
- if (obj == null) return result;
179
- if (nativeEvery && obj.every === nativeEvery) return obj.every(iterator, context);
180
- each(obj, function(value, index, list) {
181
- if (!(result = result && iterator.call(context, value, index, list))) return breaker;
98
+ _.each(obj, function(value, index) {
99
+ result = result && !!iterator.call(context, value, index);
100
+ if (!result) throw '__break__';
182
101
  });
183
- return !!result;
184
- };
185
-
186
- // Determine if at least one element in the object matches a truth test.
187
- // Delegates to **ECMAScript 5**'s native `some` if available.
188
- // Aliased as `any`.
189
- var any = _.some = _.any = function(obj, iterator, context) {
190
- iterator || (iterator = _.identity);
102
+ return result;
103
+ },
104
+
105
+ // Determine if at least one element in the object matches a truth test. Use
106
+ // Javascript 1.6's some(), if it exists.
107
+ any : function(obj, iterator, context) {
108
+ iterator = iterator || function(v) { return v; };
109
+ if (obj.some) return obj.some(iterator, context);
191
110
  var result = false;
192
- if (obj == null) return result;
193
- if (nativeSome && obj.some === nativeSome) return obj.some(iterator, context);
194
- each(obj, function(value, index, list) {
195
- if (result || (result = iterator.call(context, value, index, list))) return breaker;
111
+ _.each(obj, function(value, index) {
112
+ if (result = !!iterator.call(context, value, index)) throw '__break__';
196
113
  });
197
- return !!result;
198
- };
199
-
200
- // Determine if a given value is included in the array or object using `===`.
201
- // Aliased as `contains`.
202
- _.include = _.contains = function(obj, target) {
114
+ return result;
115
+ },
116
+
117
+ // Determine if a given value is included in the array or object,
118
+ // based on '==='.
119
+ include : function(obj, target) {
120
+ if (_.isArray(obj)) return _.indexOf(obj, target) != -1;
203
121
  var found = false;
204
- if (obj == null) return found;
205
- if (nativeIndexOf && obj.indexOf === nativeIndexOf) return obj.indexOf(target) != -1;
206
- found = any(obj, function(value) {
207
- return value === target;
122
+ _.each(obj, function(pair) {
123
+ if (pair.value === target) {
124
+ found = true;
125
+ throw '__break__';
126
+ }
208
127
  });
209
128
  return found;
210
- };
211
-
212
- // Invoke a method (with arguments) on every item in a collection.
213
- _.invoke = function(obj, method) {
214
- var args = slice.call(arguments, 2);
129
+ },
130
+
131
+ // Invoke a method with arguments on every item in a collection.
132
+ invoke : function(obj, method) {
133
+ var args = _.toArray(arguments).slice(2);
215
134
  return _.map(obj, function(value) {
216
- return (_.isFunction(method) ? method || value : value[method]).apply(value, args);
135
+ return (method ? value[method] : value).apply(value, args);
217
136
  });
218
- };
219
-
220
- // Convenience version of a common use case of `map`: fetching a property.
221
- _.pluck = function(obj, key) {
222
- return _.map(obj, function(value){ return value[key]; });
223
- };
224
-
225
- // Return the maximum element or (element-based computation).
226
- _.max = function(obj, iterator, context) {
227
- if (!iterator && _.isArray(obj) && obj[0] === +obj[0]) return Math.max.apply(Math, obj);
228
- if (!iterator && _.isEmpty(obj)) return -Infinity;
229
- var result = {computed : -Infinity};
230
- each(obj, function(value, index, list) {
231
- var computed = iterator ? iterator.call(context, value, index, list) : value;
232
- computed >= result.computed && (result = {value : value, computed : computed});
137
+ },
138
+
139
+ // Optimized version of a common use case of map: fetching a property.
140
+ pluck : function(obj, key) {
141
+ var results = [];
142
+ _.each(obj, function(value){ results.push(value[key]); });
143
+ return results;
144
+ },
145
+
146
+ // Return the maximum item or (item-based computation).
147
+ max : function(obj, iterator, context) {
148
+ if (!iterator && _.isArray(obj)) return Math.max.apply(Math, obj);
149
+ var result;
150
+ _.each(obj, function(value, index) {
151
+ var computed = iterator ? iterator.call(context, value, index) : value;
152
+ if (result == null || computed >= result.computed) result = {value : value, computed : computed};
233
153
  });
234
154
  return result.value;
235
- };
236
-
155
+ },
156
+
237
157
  // Return the minimum element (or element-based computation).
238
- _.min = function(obj, iterator, context) {
239
- if (!iterator && _.isArray(obj) && obj[0] === +obj[0]) return Math.min.apply(Math, obj);
240
- if (!iterator && _.isEmpty(obj)) return Infinity;
241
- var result = {computed : Infinity};
242
- each(obj, function(value, index, list) {
243
- var computed = iterator ? iterator.call(context, value, index, list) : value;
244
- computed < result.computed && (result = {value : value, computed : computed});
158
+ min : function(obj, iterator, context) {
159
+ if (!iterator && _.isArray(obj)) return Math.min.apply(Math, obj);
160
+ var result;
161
+ _.each(obj, function(value, index) {
162
+ var computed = iterator ? iterator.call(context, value, index) : value;
163
+ if (result == null || computed < result.computed) result = {value : value, computed : computed};
245
164
  });
246
165
  return result.value;
247
- };
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
-
260
- // Sort the object's values by a criterion produced by an iterator.
261
- _.sortBy = function(obj, val, context) {
262
- var iterator = _.isFunction(val) ? val : function(obj) { return obj[val]; };
263
- return _.pluck(_.map(obj, function(value, index, list) {
166
+ },
167
+
168
+ // Sort the object's values by a criteria produced by an iterator.
169
+ sortBy : function(obj, iterator, context) {
170
+ return _.pluck(_.map(obj, function(value, index) {
264
171
  return {
265
172
  value : value,
266
- criteria : iterator.call(context, value, index, list)
173
+ criteria : iterator.call(context, value, index)
267
174
  };
268
175
  }).sort(function(left, right) {
269
176
  var a = left.criteria, b = right.criteria;
270
- if (a === void 0) return 1;
271
- if (b === void 0) return -1;
272
177
  return a < b ? -1 : a > b ? 1 : 0;
273
178
  }), 'value');
274
- };
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
-
179
+ },
180
+
288
181
  // Use a comparator function to figure out at what index an object should
289
182
  // be inserted so as to maintain order. Uses binary search.
290
- _.sortedIndex = function(array, obj, iterator) {
291
- iterator || (iterator = _.identity);
183
+ sortedIndex : function(array, obj, iterator) {
184
+ iterator = iterator || function(val) { return val; };
292
185
  var low = 0, high = array.length;
293
186
  while (low < high) {
294
187
  var mid = (low + high) >> 1;
295
188
  iterator(array[mid]) < iterator(obj) ? low = mid + 1 : high = mid;
296
189
  }
297
190
  return low;
298
- };
299
-
300
- // Safely convert anything iterable into a real, live array.
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);
307
- };
308
-
191
+ },
192
+
193
+ // Convert anything iterable into a real, live array.
194
+ toArray : function(iterable) {
195
+ if (!iterable) return [];
196
+ if (_.isArray(iterable)) return iterable;
197
+ return _.map(iterable, function(val){ return val; });
198
+ },
199
+
309
200
  // Return the number of elements in an object.
310
- _.size = function(obj) {
311
- return _.isArray(obj) ? obj.length : _.keys(obj).length;
312
- };
313
-
314
- // Array Functions
315
- // ---------------
316
-
317
- // Get the first element of an array. Passing **n** will return the first N
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
- }
340
- };
341
-
342
- // Returns everything but the first entry of the array. Aliased as `tail`.
343
- // Especially useful on the arguments object. Passing an **index** will return
344
- // the rest of the values in the array from that index onward. The **guard**
345
- // check allows it to work with `_.map`.
346
- _.rest = _.tail = function(array, index, guard) {
347
- return slice.call(array, (index == null) || guard ? 1 : index);
348
- };
349
-
201
+ size : function(obj) {
202
+ return _.toArray(obj).length;
203
+ },
204
+
205
+ /*-------------------------- Array Functions: ------------------------------*/
206
+
207
+ // Get the first element of an array.
208
+ first : function(array) {
209
+ return array[0];
210
+ },
211
+
212
+ // Get the last element of an array.
213
+ last : function(array) {
214
+ return array[array.length - 1];
215
+ },
216
+
350
217
  // Trim out all falsy values from an array.
351
- _.compact = function(array) {
352
- return _.filter(array, function(value){ return !!value; });
353
- };
354
-
218
+ compact : function(array) {
219
+ return _.select(array, function(value){ return !!value; });
220
+ },
221
+
355
222
  // Return a completely flattened version of an array.
356
- _.flatten = function(array, shallow) {
357
- return _.reduce(array, function(memo, value) {
358
- if (_.isArray(value)) return memo.concat(shallow ? value : _.flatten(value));
359
- memo[memo.length] = value;
223
+ flatten : function(array) {
224
+ return _.inject(array, [], function(memo, value) {
225
+ if (_.isArray(value)) return memo.concat(_.flatten(value));
226
+ memo.push(value);
360
227
  return memo;
361
- }, []);
362
- };
363
-
228
+ });
229
+ },
230
+
364
231
  // Return a version of the array that does not contain the specified value(s).
365
- _.without = function(array) {
366
- return _.difference(array, slice.call(arguments, 1));
367
- };
368
-
232
+ without : function(array) {
233
+ var values = array.slice.call(arguments, 0);
234
+ return _.select(array, function(value){ return !_.include(values, value); });
235
+ },
236
+
369
237
  // Produce a duplicate-free version of the array. If the array has already
370
238
  // been sorted, you have the option of using a faster algorithm.
371
- // Aliased as `unique`.
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
- }
239
+ uniq : function(array, isSorted) {
240
+ return _.inject(array, [], function(memo, el, i) {
241
+ if (0 == i || (isSorted ? _.last(memo) != el : !_.include(memo, el))) memo.push(el);
382
242
  return memo;
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));
391
- };
392
-
393
- // Produce an array that contains every item shared between all the
394
- // passed-in arrays. (Aliased as "intersect" for back-compat.)
395
- _.intersection = _.intersect = function(array) {
396
- var rest = slice.call(arguments, 1);
397
- return _.filter(_.uniq(array), function(item) {
398
- return _.every(rest, function(other) {
243
+ });
244
+ },
245
+
246
+ // Produce an array that contains every item shared between all the
247
+ // passed-in arrays.
248
+ intersect : function(array) {
249
+ var rest = _.toArray(arguments).slice(1);
250
+ return _.select(_.uniq(array), function(item) {
251
+ return _.all(rest, function(other) {
399
252
  return _.indexOf(other, item) >= 0;
400
253
  });
401
254
  });
402
- };
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
-
255
+ },
256
+
411
257
  // Zip together multiple lists into a single array -- elements that share
412
258
  // an index go together.
413
- _.zip = function() {
414
- var args = slice.call(arguments);
259
+ zip : function() {
260
+ var args = _.toArray(arguments);
415
261
  var length = _.max(_.pluck(args, 'length'));
416
262
  var results = new Array(length);
417
- for (var i = 0; i < length; i++) results[i] = _.pluck(args, "" + i);
263
+ for (var i=0; i<length; i++) results[i] = _.pluck(args, String(i));
418
264
  return results;
419
- };
420
-
421
- // If the browser doesn't supply us with indexOf (I'm looking at you, **MSIE**),
422
- // we need this function. Return the position of the first occurrence of an
265
+ },
266
+
267
+ // If the browser doesn't supply us with indexOf (I'm looking at you, MSIE),
268
+ // we need this function. Return the position of the first occurence of an
423
269
  // item in an array, or -1 if the item is not included in the array.
424
- // Delegates to **ECMAScript 5**'s native `indexOf` if available.
425
- // If the array is large and already in sort order, pass `true`
426
- // for **isSorted** to use binary search.
427
- _.indexOf = function(array, item, isSorted) {
428
- if (array == null) return -1;
429
- var i, l;
430
- if (isSorted) {
431
- i = _.sortedIndex(array, item);
432
- return array[i] === item ? i : -1;
433
- }
434
- if (nativeIndexOf && array.indexOf === nativeIndexOf) return array.indexOf(item);
435
- for (i = 0, l = array.length; i < l; i++) if (i in array && array[i] === item) return i;
436
- return -1;
437
- };
438
-
439
- // Delegates to **ECMAScript 5**'s native `lastIndexOf` if available.
440
- _.lastIndexOf = function(array, item) {
441
- if (array == null) return -1;
442
- if (nativeLastIndexOf && array.lastIndexOf === nativeLastIndexOf) return array.lastIndexOf(item);
443
- var i = array.length;
444
- while (i--) if (i in array && array[i] === item) return i;
270
+ indexOf : function(array, item) {
271
+ if (array.indexOf) return array.indexOf(item);
272
+ var length = array.length;
273
+ for (i=0; i<length; i++) if (array[i] === item) return i;
445
274
  return -1;
446
- };
447
-
448
- // Generate an integer Array containing an arithmetic progression. A port of
449
- // the native Python `range()` function. See
450
- // [the Python documentation](http://docs.python.org/library/functions.html#range).
451
- _.range = function(start, stop, step) {
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) {
463
- range[idx++] = start;
464
- start += step;
465
- }
466
-
467
- return range;
468
- };
469
-
470
- // Function (ahem) Functions
471
- // ------------------
472
-
473
- // Reusable constructor function for prototype setting.
474
- var ctor = function(){};
475
-
476
- // Create a function bound to a given object (assigning `this`, and arguments,
477
- // optionally). Binding with arguments is also known as `curry`.
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;
492
- };
493
- };
494
-
495
- // Bind all of an object's methods to that object. Useful for ensuring that
496
- // all callbacks defined on an object belong to it.
497
- _.bindAll = function(obj) {
498
- var funcs = slice.call(arguments, 1);
499
- if (funcs.length == 0) funcs = _.functions(obj);
500
- each(funcs, function(f) { obj[f] = _.bind(obj[f], obj); });
501
- return obj;
502
- };
503
-
504
- // Memoize an expensive function by storing its results.
505
- _.memoize = function(func, hasher) {
506
- var memo = {};
507
- hasher || (hasher = _.identity);
275
+ },
276
+
277
+ /* ----------------------- Function Functions: -----------------------------*/
278
+
279
+ // Create a function bound to a given object (assigning 'this', and arguments,
280
+ // optionally). Binding with arguments is also known as 'curry'.
281
+ bind : function(func, context) {
282
+ if (!context) return func;
283
+ var args = _.toArray(arguments).slice(2);
508
284
  return function() {
509
- var key = hasher.apply(this, arguments);
510
- return _.has(memo, key) ? memo[key] : (memo[key] = func.apply(this, arguments));
285
+ var a = args.concat(_.toArray(arguments));
286
+ return func.apply(context, a);
511
287
  };
512
- };
513
-
288
+ },
289
+
290
+ // Bind all of an object's methods to that object. Useful for ensuring that
291
+ // all callbacks defined on an object belong to it.
292
+ bindAll : function() {
293
+ var args = _.toArray(arguments);
294
+ var context = args.pop();
295
+ _.each(args, function(methodName) {
296
+ context[methodName] = _.bind(context[methodName], context);
297
+ });
298
+ },
299
+
514
300
  // Delays a function for the given number of milliseconds, and then calls
515
301
  // it with the arguments supplied.
516
- _.delay = function(func, wait) {
517
- var args = slice.call(arguments, 2);
518
- return setTimeout(function(){ return func.apply(null, args); }, wait);
519
- };
520
-
521
- // Defers a function, scheduling it to run after the current call stack has
302
+ delay : function(func, wait) {
303
+ var args = _.toArray(arguments).slice(2);
304
+ return window.setTimeout(function(){ return func.apply(func, args); }, wait);
305
+ },
306
+
307
+ // Defers a function, scheduling it to run after the current call stack has
522
308
  // cleared.
523
- _.defer = function(func) {
524
- return _.delay.apply(_, [func, 1].concat(slice.call(arguments, 1)));
525
- };
526
-
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);
532
- return function() {
533
- context = this; args = arguments;
534
- var later = function() {
535
- timeout = null;
536
- if (more) func.apply(context, args);
537
- whenDone();
538
- };
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;
548
- };
549
- };
550
-
551
- // Returns a function, that, as long as it continues to be invoked, will not
552
- // be triggered. The function will be called after it stops being called for
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
- };
578
- };
579
-
580
- // Returns the first function passed as an argument to the second,
581
- // allowing you to adjust arguments, run code before and after, and
309
+ defer : function(func) {
310
+ return _.delay.apply(_, [func, 1].concat(_.toArray(arguments).slice(1)));
311
+ },
312
+
313
+ // Returns the first function passed as an argument to the second,
314
+ // allowing you to adjust arguments, run code before and after, and
582
315
  // conditionally execute the original function.
583
- _.wrap = function(func, wrapper) {
584
- return function() {
585
- var args = [func].concat(slice.call(arguments, 0));
586
- return wrapper.apply(this, args);
587
- };
588
- };
589
-
590
- // Returns a function that is the composition of a list of functions, each
591
- // consuming the return value of the function that follows.
592
- _.compose = function() {
593
- var funcs = arguments;
316
+ wrap : function(func, wrapper) {
594
317
  return function() {
595
- var args = arguments;
596
- for (var i = funcs.length - 1; i >= 0; i--) {
597
- args = [funcs[i].apply(this, args)];
598
- }
599
- return args[0];
318
+ var args = [func].concat(_.toArray(arguments));
319
+ return wrapper.apply(wrapper, args);
600
320
  };
601
- };
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
-
611
- // Object Functions
612
- // ----------------
613
-
321
+ },
322
+
323
+ /* ------------------------- Object Functions: ---------------------------- */
324
+
614
325
  // Retrieve the names of an object's properties.
615
- // Delegates to **ECMAScript 5**'s native `Object.keys`
616
- _.keys = nativeKeys || function(obj) {
617
- if (obj !== Object(obj)) throw new TypeError('Invalid object');
618
- var keys = [];
619
- for (var key in obj) if (_.has(obj, key)) keys[keys.length] = key;
620
- return keys;
621
- };
622
-
326
+ keys : function(obj) {
327
+ return _.pluck(obj, 'key');
328
+ },
329
+
623
330
  // Retrieve the values of an object's properties.
624
- _.values = function(obj) {
625
- return _.map(obj, _.identity);
626
- };
627
-
628
- // Return a sorted list of the function names available on the object.
629
- // Aliased as `methods`
630
- _.functions = _.methods = function(obj) {
631
- var names = [];
632
- for (var key in obj) {
633
- if (_.isFunction(obj[key])) names.push(key);
634
- }
635
- return names.sort();
636
- };
637
-
638
- // Extend a given object with all the properties in passed-in object(s).
639
- _.extend = function(obj) {
640
- each(slice.call(arguments, 1), function(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
- }
663
- });
664
- return obj;
665
- };
666
-
331
+ values : function(obj) {
332
+ return _.pluck(obj, 'value');
333
+ },
334
+
335
+ // Extend a given object with all of the properties in a source object.
336
+ extend : function(destination, source) {
337
+ for (var property in source) destination[property] = source[property];
338
+ return destination;
339
+ },
340
+
667
341
  // Create a (shallow-cloned) duplicate of an object.
668
- _.clone = function(obj) {
669
- if (!_.isObject(obj)) return obj;
670
- return _.isArray(obj) ? obj.slice() : _.extend({}, obj);
671
- };
672
-
673
- // Invokes interceptor with the obj, and then returns obj.
674
- // The primary purpose of this method is to "tap into" a method chain, in
675
- // order to perform operations on intermediate results within the chain.
676
- _.tap = function(obj, interceptor) {
677
- interceptor(obj);
678
- return obj;
679
- };
680
-
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;
688
- // Unwrap any wrapped objects.
689
- if (a._chain) a = a._wrapped;
690
- if (b._chain) b = b._wrapped;
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
-
342
+ clone : function(obj) {
343
+ return _.extend({}, obj);
344
+ },
345
+
769
346
  // Perform a deep comparison to check if two objects are equal.
770
- _.isEqual = function(a, b) {
771
- return eq(a, b, []);
772
- };
773
-
774
- // Is a given array, string, or object empty?
775
- // An "empty" object has no enumerable own-properties.
776
- _.isEmpty = function(obj) {
777
- if (obj == null) return true;
778
- if (_.isArray(obj) || _.isString(obj)) return obj.length === 0;
779
- for (var key in obj) if (_.has(obj, key)) return false;
347
+ isEqual : function(a, b) {
348
+ // Check object identity.
349
+ if (a === b) return true;
350
+ // Different types?
351
+ var atype = typeof(a), btype = typeof(b);
352
+ if (atype != btype) return false;
353
+ // Basic equality test (watch out for coercions).
354
+ if (a == b) return true;
355
+ // One of them implements an isEqual()?
356
+ if (a.isEqual) return a.isEqual(b);
357
+ // If a is not an object by this point, we can't handle it.
358
+ if (atype !== 'object') return false;
359
+ // Nothing else worked, deep compare the contents.
360
+ var aKeys = _.keys(a), bKeys = _.keys(b);
361
+ // Different object sizes?
362
+ if (aKeys.length != bKeys.length) return false;
363
+ // Recursive comparison of contents.
364
+ for (var key in a) if (!_.isEqual(a[key], b[key])) return false;
780
365
  return true;
781
- };
782
-
366
+ },
367
+
783
368
  // Is a given value a DOM element?
784
- _.isElement = function(obj) {
369
+ isElement : function(obj) {
785
370
  return !!(obj && obj.nodeType == 1);
786
- };
787
-
788
- // Is a given value an array?
789
- // Delegates to ECMA5's native Array.isArray
790
- _.isArray = nativeIsArray || function(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);
797
- };
798
-
799
- // Is a given variable an arguments object?
800
- _.isArguments = function(obj) {
801
- return toString.call(obj) == '[object Arguments]';
802
- };
803
- if (!_.isArguments(arguments)) {
804
- _.isArguments = function(obj) {
805
- return !!(obj && _.has(obj, 'callee'));
806
- };
807
- }
808
-
809
- // Is a given value a function?
810
- _.isFunction = function(obj) {
811
- return toString.call(obj) == '[object Function]';
812
- };
813
-
814
- // Is a given value a string?
815
- _.isString = function(obj) {
816
- return toString.call(obj) == '[object String]';
817
- };
818
-
819
- // Is a given value a number?
820
- _.isNumber = function(obj) {
821
- return toString.call(obj) == '[object Number]';
822
- };
823
-
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`?
830
- _.isNaN = function(obj) {
831
- // `NaN` is the only value for which `===` is not reflexive.
832
- return obj !== obj;
833
- };
834
-
835
- // Is a given value a boolean?
836
- _.isBoolean = function(obj) {
837
- return obj === true || obj === false || toString.call(obj) == '[object Boolean]';
838
- };
839
-
840
- // Is a given value a date?
841
- _.isDate = function(obj) {
842
- return toString.call(obj) == '[object Date]';
843
- };
844
-
845
- // Is the given value a regular expression?
846
- _.isRegExp = function(obj) {
847
- return toString.call(obj) == '[object RegExp]';
848
- };
849
-
850
- // Is a given value equal to null?
851
- _.isNull = function(obj) {
852
- return obj === null;
853
- };
854
-
371
+ },
372
+
373
+ // Is a given value a real Array?
374
+ isArray : function(obj) {
375
+ return Object.prototype.toString.call(obj) == '[object Array]';
376
+ },
377
+
378
+ // Is a given value a Function?
379
+ isFunction : function(obj) {
380
+ return typeof obj == 'function';
381
+ },
382
+
855
383
  // Is a given variable undefined?
856
- _.isUndefined = function(obj) {
857
- return obj === void 0;
858
- };
859
-
860
- // Has own property?
861
- _.has = function(obj, key) {
862
- return hasOwnProperty.call(obj, key);
863
- };
864
-
865
- // Utility Functions
866
- // -----------------
867
-
868
- // Run Underscore.js in *noConflict* mode, returning the `_` variable to its
869
- // previous owner. Returns a reference to the Underscore object.
870
- _.noConflict = function() {
871
- root._ = previousUnderscore;
872
- return this;
873
- };
874
-
875
- // Keep the identity function around for default iterators.
876
- _.identity = function(value) {
877
- return value;
878
- };
879
-
880
- // Run a function **n** times.
881
- _.times = function (n, iterator, context) {
882
- for (var i = 0; i < n; i++) iterator.call(context, i);
883
- };
884
-
885
- // Escape a string for HTML interpolation.
886
- _.escape = function(string) {
887
- return (''+string).replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;').replace(/"/g, '&quot;').replace(/'/g, '&#x27;').replace(/\//g,'&#x2F;');
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
-
898
- // Add your own custom functions to the Underscore object, ensuring that
899
- // they're correctly added to the OOP wrapper as well.
900
- _.mixin = function(obj) {
901
- each(_.functions(obj), function(name){
902
- addToWrapper(name, _[name] = obj[name]);
903
- });
904
- };
905
-
384
+ isUndefined : function(obj) {
385
+ return typeof obj == 'undefined';
386
+ },
387
+
388
+ /* -------------------------- Utility Functions: -------------------------- */
389
+
906
390
  // Generate a unique integer id (unique within the entire client session).
907
391
  // Useful for temporary DOM ids.
908
- var idCounter = 0;
909
- _.uniqueId = function(prefix) {
910
- var id = idCounter++;
392
+ uniqueId : function(prefix) {
393
+ var id = this._idCounter = (this._idCounter || 0) + 1;
911
394
  return prefix ? prefix + id : id;
912
- };
913
-
914
- // By default, Underscore uses ERB-style template delimiters, change the
915
- // following template settings to use alternative delimiters.
916
- _.templateSettings = {
917
- evaluate : /<%([\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
- });
949
- };
950
-
951
- // JavaScript micro-templating, similar to John Resig's implementation.
952
- // Underscore templating handles arbitrary delimiters, preserves whitespace,
953
- // and correctly escapes quotes within interpolated code.
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();
998
- };
999
-
1000
- // The OOP Wrapper
1001
- // ---------------
1002
-
1003
- // If Underscore is called as a function, it returns a wrapped object that
1004
- // can be used OO-style. This wrapper holds altered versions of all the
1005
- // underscore functions. Wrapped objects may be chained.
1006
- var wrapper = function(obj) { this._wrapped = obj; };
1007
-
1008
- // Expose `wrapper.prototype` as `_.prototype`
1009
- _.prototype = wrapper.prototype;
1010
-
1011
- // Helper function to continue chaining intermediate results.
1012
- var result = function(obj, chain) {
1013
- return chain ? _(obj).chain() : obj;
1014
- };
1015
-
1016
- // A method to easily add functions to the OOP wrapper.
1017
- var addToWrapper = function(name, func) {
1018
- wrapper.prototype[name] = function() {
1019
- var args = slice.call(arguments);
1020
- unshift.call(args, this._wrapped);
1021
- return result(func.apply(_, args), this._chain);
1022
- };
1023
- };
1024
-
1025
- // Add all of the Underscore functions to the wrapper object.
1026
- _.mixin(_);
1027
-
1028
- // Add all mutator Array functions to the wrapper.
1029
- each(['pop', 'push', 'reverse', 'shift', 'sort', 'splice', 'unshift'], function(name) {
1030
- var method = ArrayProto[name];
1031
- wrapper.prototype[name] = function() {
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);
1037
- };
1038
- });
1039
-
1040
- // Add all accessor Array functions to the wrapper.
1041
- each(['concat', 'join', 'slice'], function(name) {
1042
- var method = ArrayProto[name];
1043
- wrapper.prototype[name] = function() {
1044
- return result(method.apply(this._wrapped, arguments), this._chain);
1045
- };
1046
- });
1047
-
1048
- // Start chaining a wrapped Underscore object.
1049
- wrapper.prototype.chain = function() {
1050
- this._chain = true;
1051
- return this;
1052
- };
1053
-
1054
- // Extracts the result from a wrapped and chained object.
1055
- wrapper.prototype.value = function() {
1056
- return this._wrapped;
1057
- };
1058
-
1059
- }).call(this);
395
+ },
396
+
397
+ // Javascript templating a-la ERB, pilfered from John Resig's
398
+ // "Secrets of the Javascript Ninja", page 83.
399
+ template : function(str, data) {
400
+ var fn = new Function('obj',
401
+ 'var p=[],print=function(){p.push.apply(p,arguments);};' +
402
+ 'with(obj){p.push(\'' +
403
+ str
404
+ .replace(/[\r\t\n]/g, " ")
405
+ .split("<%").join("\t")
406
+ .replace(/((^|%>)[^\t]*)'/g, "$1\r")
407
+ .replace(/\t=(.*?)%>/g, "',$1,'")
408
+ .split("\t").join("');")
409
+ .split("%>").join("p.push('")
410
+ .split("\r").join("\\'")
411
+ + "');}return p.join('');");
412
+ return data ? fn(data) : fn;
413
+ }
414
+
415
+ };