1
2
3
4
5
6
7
8
9
10
11
12
13
14 root: this
15
16
17
18 previousUnderscore: root._
19
20
21
22 breaker: if typeof(StopIteration) is 'undefined' then '__break__' else StopIteration
23
24
25
26 escapeRegExp: (string) -> string.replace(/([.*+?^${}()|[\]\/\\])/g, '\\$1')
27
28
29
30 ArrayProto: Array.prototype
31 ObjProto: Object.prototype
32
33
34
35 slice: ArrayProto.slice
36 unshift: ArrayProto.unshift
37 toString: ObjProto.toString
38 hasOwnProperty: ObjProto.hasOwnProperty
39 propertyIsEnumerable: ObjProto.propertyIsEnumerable
40
41
42
43 nativeForEach: ArrayProto.forEach
44 nativeMap: ArrayProto.map
45 nativeReduce: ArrayProto.reduce
46 nativeReduceRight: ArrayProto.reduceRight
47 nativeFilter: ArrayProto.filter
48 nativeEvery: ArrayProto.every
49 nativeSome: ArrayProto.some
50 nativeIndexOf: ArrayProto.indexOf
51 nativeLastIndexOf: ArrayProto.lastIndexOf
52 nativeIsArray: Array.isArray
53 nativeKeys: Object.keys
54
55
56
57 _: (obj) -> new wrapper(obj)
58
59
60
61 if typeof(exports) != 'undefined' then exports._: _
62
63
64
65 root._: _
66
67
68
69 _.VERSION: '0.6.0'
70
71
72
73
74
75
76 _.each: (obj, iterator, context) ->
77 try
78 if nativeForEach and obj.forEach is nativeForEach
79 obj.forEach iterator, context
80 else if _.isNumber obj.length
81 iterator.call(context, obj[i], i, obj) for i in [0...obj.length]
82 else
83 iterator.call(context, val, key, obj) for key, val of obj
84 catch e
85 throw e if e isnt breaker
86 obj
87
88
89
90
91 _.map: (obj, iterator, context) ->
92 return obj.map(iterator, context) if nativeMap and obj.map is nativeMap
93 results: []
94 _.each obj, (value, index, list) ->
95 results.push iterator.call context, value, index, list
96 results
97
98
99
100
101 _.reduce: (obj, memo, iterator, context) ->
102 return obj.reduce(_.bind(iterator, context), memo) if nativeReduce and obj.reduce is nativeReduce
103 _.each obj, (value, index, list) ->
104 memo: iterator.call context, memo, value, index, list
105 memo
106
107
108
109
110 _.reduceRight: (obj, memo, iterator, context) ->
111 return obj.reduceRight(_.bind(iterator, context), memo) if nativeReduceRight and obj.reduceRight is nativeReduceRight
112 _.each _.clone(_.toArray(obj)).reverse(), (value, index) ->
113 memo: iterator.call context, memo, value, index, obj
114 memo
115
116
117
118 _.detect: (obj, iterator, context) ->
119 result: null
120 _.each obj, (value, index, list) ->
121 if iterator.call context, value, index, list
122 result: value
123 _.breakLoop()
124 result
125
126
127
128
129 _.filter: (obj, iterator, context) ->
130 return obj.filter iterator, context if nativeFilter and obj.filter is nativeFilter
131 results: []
132 _.each obj, (value, index, list) ->
133 results.push value if iterator.call context, value, index, list
134 results
135
136
137
138 _.reject: (obj, iterator, context) ->
139 results: []
140 _.each obj, (value, index, list) ->
141 results.push value if not iterator.call context, value, index, list
142 results
143
144
145
146
147 _.every: (obj, iterator, context) ->
148 iterator ||= _.identity
149 return obj.every iterator, context if nativeEvery and obj.every is nativeEvery
150 result: true
151 _.each obj, (value, index, list) ->
152 _.breakLoop() unless (result: result and iterator.call(context, value, index, list))
153 result
154
155
156
157
158 _.some: (obj, iterator, context) ->
159 iterator ||= _.identity
160 return obj.some iterator, context if nativeSome and obj.some is nativeSome
161 result: false
162 _.each obj, (value, index, list) ->
163 _.breakLoop() if (result: iterator.call(context, value, index, list))
164 result
165
166
167
168
169 _.include: (obj, target) ->
170 return _.indexOf(obj, target) isnt -1 if nativeIndexOf and obj.indexOf is nativeIndexOf
171 for key, val of obj
172 return true if val is target
173 false
174
175
176
177 _.invoke: (obj, method) ->
178 args: _.rest arguments, 2
179 (if method then val[method] else val).apply(val, args) for val in obj
180
181
182
183 _.pluck: (obj, key) ->
184 _.map(obj, (val) -> val[key])
185
186
187
188 _.max: (obj, iterator, context) ->
189 return Math.max.apply(Math, obj) if not iterator and _.isArray(obj)
190 result: {computed: -Infinity}
191 _.each obj, (value, index, list) ->
192 computed: if iterator then iterator.call(context, value, index, list) else value
193 computed >= result.computed and (result: {value: value, computed: computed})
194 result.value
195
196
197
198 _.min: (obj, iterator, context) ->
199 return Math.min.apply(Math, obj) if not iterator and _.isArray(obj)
200 result: {computed: Infinity}
201 _.each obj, (value, index, list) ->
202 computed: if iterator then iterator.call(context, value, index, list) else value
203 computed < result.computed and (result: {value: value, computed: computed})
204 result.value
205
206
207
208 _.sortBy: (obj, iterator, context) ->
209 _.pluck(((_.map obj, (value, index, list) ->
210 {value: value, criteria: iterator.call(context, value, index, list)}
211 ).sort((left, right) ->
212 a: left.criteria; b: right.criteria
213 if a < b then -1 else if a > b then 1 else 0
214 )), 'value')
215
216
217
218
219 _.sortedIndex: (array, obj, iterator) ->
220 iterator ||= _.identity
221 low: 0
222 high: array.length
223 while low < high
224 mid: (low + high) >> 1
225 if iterator(array[mid]) < iterator(obj) then low: mid + 1 else high: mid
226 low
227
228
229
230 _.toArray: (iterable) ->
231 return [] if (!iterable)
232 return iterable.toArray() if (iterable.toArray)
233 return iterable if (_.isArray(iterable))
234 return slice.call(iterable) if (_.isArguments(iterable))
235 _.values(iterable)
236
237
238
239 _.size: (obj) -> _.toArray(obj).length
240
241
242
243
244
245
246
247 _.first: (array, n, guard) ->
248 if n and not guard then slice.call(array, 0, n) else array[0]
249
250
251
252
253
254
255 _.rest: (array, index, guard) ->
256 slice.call(array, if _.isUndefined(index) or guard then 1 else index)
257
258
259
260 _.last: (array) -> array[array.length - 1]
261
262
263
264 _.compact: (array) -> item for item in array when item
265
266
267
268 _.flatten: (array) ->
269 _.reduce array, [], (memo, value) ->
270 return memo.concat(_.flatten(value)) if _.isArray value
271 memo.push value
272 memo
273
274
275
276 _.without: (array) ->
277 values: _.rest arguments
278 val for val in _.toArray(array) when not _.include values, val
279
280
281
282
283 _.uniq: (array, isSorted) ->
284 memo: []
285 for el, i in _.toArray array
286 memo.push el if i is 0 || (if isSorted is true then _.last(memo) isnt el else not _.include(memo, el))
287 memo
288
289
290
291
292 _.intersect: (array) ->
293 rest: _.rest arguments
294 _.select _.uniq(array), (item) ->
295 _.all rest, (other) ->
296 _.indexOf(other, item) >= 0
297
298
299
300
301 _.zip: ->
302 length: _.max _.pluck arguments, 'length'
303 results: new Array length
304 for i in [0...length]
305 results[i]: _.pluck arguments, String i
306 results
307
308
309
310
311
312 _.indexOf: (array, item) ->
313 return array.indexOf item if nativeIndexOf and array.indexOf is nativeIndexOf
314 i: 0; l: array.length
315 while l - i
316 if array[i] is item then return i else i++
317 -1
318
319
320
321
322 _.lastIndexOf: (array, item) ->
323 return array.lastIndexOf(item) if nativeLastIndexOf and array.lastIndexOf is nativeLastIndexOf
324 i: array.length
325 while i
326 if array[i] is item then return i else i--
327 -1
328
329
330
331
332
333 _.range: (start, stop, step) ->
334 a: arguments
335 solo: a.length <= 1
336 i: start: if solo then 0 else a[0]
337 stop: if solo then a[0] else a[1]
338 step: a[2] or 1
339 len: Math.ceil((stop - start) / step)
340 return [] if len <= 0
341 range: new Array len
342 idx: 0
343 while true
344 return range if (if step > 0 then i - stop else stop - i) >= 0
345 range[idx]: i
346 idx++
347 i+= step
348
349
350
351
352
353
354 _.bind: (func, obj) ->
355 args: _.rest arguments, 2
356 -> func.apply obj or root, args.concat arguments
357
358
359
360
361 _.bindAll: (obj) ->
362 funcs: if arguments.length > 1 then _.rest(arguments) else _.functions(obj)
363 _.each funcs, (f) -> obj[f]: _.bind obj[f], obj
364 obj
365
366
367
368
369 _.delay: (func, wait) ->
370 args: _.rest arguments, 2
371 setTimeout((-> func.apply(func, args)), wait)
372
373
374
375
376 _.defer: (func) ->
377 _.delay.apply _, [func, 1].concat _.rest arguments
378
379
380
381
382
383 _.wrap: (func, wrapper) ->
384 -> wrapper.apply wrapper, [func].concat arguments
385
386
387
388
389 _.compose: ->
390 funcs: arguments
391 ->
392 args: arguments
393 for i in [(funcs.length - 1)..0]
394 args: [funcs[i].apply(this, args)]
395 args[0]
396
397
398
399
400
401 _.keys: nativeKeys or (obj) ->
402 return _.range 0, obj.length if _.isArray(obj)
403 key for key, val of obj
404
405
406
407 _.values: (obj) ->
408 _.map obj, _.identity
409
410
411
412 _.functions: (obj) ->
413 _.filter(_.keys(obj), (key) -> _.isFunction(obj[key])).sort()
414
415
416
417 _.extend: (destination, source) ->
418 (destination[key]: val) for key, val of source
419 destination
420
421
422
423 _.clone: (obj) ->
424 return obj.slice 0 if _.isArray obj
425 _.extend {}, obj
426
427
428
429
430 _.tap: (obj, interceptor) ->
431 interceptor obj
432 obj
433
434
435
436 _.isEqual: (a, b) ->
437
438 return true if a is b
439
440 atype: typeof(a); btype: typeof(b)
441 return false if atype isnt btype
442
443 return true if `a == b`
444
445 return false if (!a and b) or (a and !b)
446
447 return a.isEqual(b) if a.isEqual
448
449 return a.getTime() is b.getTime() if _.isDate(a) and _.isDate(b)
450
451 return true if _.isNaN(a) and _.isNaN(b)
452
453 if _.isRegExp(a) and _.isRegExp(b)
454 return a.source is b.source and
455 a.global is b.global and
456 a.ignoreCase is b.ignoreCase and
457 a.multiline is b.multiline
458
459 return false if atype isnt 'object'
460
461 return false if a.length and (a.length isnt b.length)
462
463 aKeys: _.keys(a); bKeys: _.keys(b)
464
465 return false if aKeys.length isnt bKeys.length
466
467 (return false) for key, val of a when !_.isEqual(val, b[key])
468 true
469
470
471
472 _.isEmpty: (obj) ->
473 return obj.length is 0 if _.isArray obj
474 (return false) for key of obj when hasOwnProperty.call(obj, key)
475 true
476
477
478
479 _.isElement: (obj) -> obj and obj.nodeType is 1
480
481
482
483 _.isArray: nativeIsArray or (obj) -> !!(obj and obj.concat and obj.unshift)
484
485
486
487 _.isArguments: (obj) -> obj and _.isNumber(obj.length) and not obj.concat and
488 not obj.substr and not obj.apply and not propertyIsEnumerable.call(obj, 'length')
489
490
491
492 _.isFunction: (obj) -> !!(obj and obj.constructor and obj.call and obj.apply)
493
494
495
496 _.isString: (obj) -> !!(obj is '' or (obj and obj.charCodeAt and obj.substr))
497
498
499
500 _.isNumber: (obj) -> (obj is +obj) or toString.call(obj) is '[object Number]'
501
502
503
504 _.isBoolean: (obj) -> obj is true or obj is false
505
506
507
508 _.isDate: (obj) -> !!(obj and obj.getTimezoneOffset and obj.setUTCFullYear)
509
510
511
512 _.isRegExp: (obj) -> !!(obj and obj.exec and (obj.ignoreCase or obj.ignoreCase is false))
513
514
515
516
517 _.isNaN: (obj) -> _.isNumber(obj) and window.isNaN(obj)
518
519
520
521 _.isNull: (obj) -> obj is null
522
523
524
525 _.isUndefined: (obj) -> typeof obj is 'undefined'
526
527
528
529
530
531
532 _.noConflict: ->
533 root._: previousUnderscore
534 this
535
536
537
538 _.identity: (value) -> value
539
540
541
542 _.times: (n, iterator, context) ->
543 iterator.call(context, i) for i in [0...n]
544
545
546
547 _.breakLoop: -> throw breaker
548
549
550
551
552 _.mixin: (obj) ->
553 for name in _.functions(obj)
554 addToWrapper name, _[name]: obj[name]
555
556
557
558
559 idCounter: 0
560 _.uniqueId: (prefix) ->
561 (prefix or '') + idCounter++
562
563
564
565
566 _.templateSettings: {
567 start: '<%'
568 end: '%>'
569 interpolate: /<%=(.+?)%>/g
570 }
571
572
573
574
575
576 _.template: (str, data) ->
577 c: _.templateSettings
578 endMatch: new RegExp("'(?=[^"+c.end.substr(0, 1)+"]*"+escapeRegExp(c.end)+")","g")
579 fn: new Function 'obj',
580 'var p=[],print=function(){p.push.apply(p,arguments);};' +
581 'with(obj){p.push(\'' +
582 str.replace(/[\r\t\n]/g, " ")
583 .replace(endMatch,"\t")
584 .split("'").join("\\'")
585 .split("\t").join("'")
586 .replace(c.interpolate, "',$1,'")
587 .split(c.start).join("');")
588 .split(c.end).join("p.push('") +
589 "');}return p.join('');"
590 if data then fn(data) else fn
591
592
593
594
595 _.forEach: _.each
596 _.foldl: _.inject: _.reduce
597 _.foldr: _.reduceRight
598 _.select: _.filter
599 _.all: _.every
600 _.any: _.some
601 _.head: _.first
602 _.tail: _.rest
603 _.methods: _.functions
604
605
606
607
608
609
610
611 wrapper: (obj) ->
612 this._wrapped: obj
613 this
614
615
616
617 result: (obj, chain) ->
618 if chain then _(obj).chain() else obj
619
620
621
622 addToWrapper: (name, func) ->
623 wrapper.prototype[name]: ->
624 args: _.toArray arguments
625 unshift.call args, this._wrapped
626 result func.apply(_, args), this._chain
627
628
629
630 _.mixin _
631
632
633
634 _.each ['pop', 'push', 'reverse', 'shift', 'sort', 'splice', 'unshift'], (name) ->
635 method: Array.prototype[name]
636 wrapper.prototype[name]: ->
637 method.apply(this._wrapped, arguments)
638 result(this._wrapped, this._chain)
639
640
641
642 _.each ['concat', 'join', 'slice'], (name) ->
643 method: Array.prototype[name]
644 wrapper.prototype[name]: ->
645 result(method.apply(this._wrapped, arguments), this._chain)
646
647
648
649 wrapper::chain: ->
650 this._chain: true
651 this
652
653
654
655 wrapper::value: -> this._wrapped