-
+
Underscore.coffee
-
@@ -28,600 +28,641 @@
18 previousUnderscore: root._
19 20
- 21 # If Underscore is called as a function, it returns a wrapped object that
- 22 # can be used OO-style. This wrapper holds altered versions of all the
- 23 # underscore functions. Wrapped objects may be chained.
- 24 wrapper:(obj)->
- 25 this._wrapped: obj
- 26 this
+ 21 # Establish the object that gets thrown to break out of a loop iteration.
+ 22 breaker:iftypeof(StopIteration) is'undefined'then'__break__'else StopIteration
+ 23
+ 24
+ 25 # Quick regexp-escaping function, because JS doesn't have RegExp.escape().
+ 26 escapeRegExp:(string)-> string.replace(/([.*+?^${}()|[\]\/\\])/g, '\\$1')
27 28
- 29 # Establish the object that gets thrown to break out of a loop iteration.
- 30 breaker:iftypeof(StopIteration) is'undefined'then'__break__'else StopIteration
- 31
+ 29 # Save bytes in the minified (but not gzipped) version:
+ 30 ArrayProto: Array.prototype
+ 31 ObjProto: Object.prototype
32
- 33 # Create a safe reference to the Underscore object forreference below.
- 34 _:root._:(obj)->newwrapper(obj)
- 35
- 36
- 37 # Export the Underscore object for CommonJS.
- 38 iftypeof(exports) !='undefined'thenexports._: _
- 39
+ 33
+ 34 #Create quick reference variables for speed access to core prototypes.
+ 35 slice: ArrayProto.slice
+ 36 unshift: ArrayProto.unshift
+ 37 toString: ObjProto.toString
+ 38 hasOwnProperty: ObjProto.hasOwnProperty
+ 39 propertyIsEnumerable: ObjProto.propertyIsEnumerable
40
- 41 # Create quick reference variables for speed access to core prototypes.
- 42 slice:Array::slice
- 43 unshift:Array::unshift
- 44 toString:Object::toString
- 45 hasOwnProperty:Object::hasOwnProperty
- 46 propertyIsEnumerable:Object::propertyIsEnumerable
- 47
- 48
- 49 # Current version.
- 50 _.VERSION:'0.5.8'
- 51
- 52
- 53 # ------------------------ Collection Functions: ---------------------------
+ 41
+ 42 # All ECMA5 native implementations we hope to use are declared here.
+ 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 # The cornerstone, an each implementation.
- 56 # Handles objects implementing forEach, arrays, and raw objects.
- 57 _.each:(obj, iterator, context)->
- 58 index:0
- 59 try
- 60 return obj.forEach(iterator, context) if obj.forEach
- 61 if _.isNumber(obj.length)
- 62 return iterator.call(context, obj[i], i, obj) for i in [0...obj.length]
- 63 iterator.call(context, val, key, obj) for key, val of obj
- 64 catch e
- 65 throw e if e isnt breaker
- 66 obj
+ 55
+ 56 # Create a safe reference to the Underscore object for use below.
+ 57 _:(obj)->newwrapper(obj)
+ 58
+ 59
+ 60 # Export the Underscore object for CommonJS.
+ 61 iftypeof(exports) !='undefined'thenexports._: _
+ 62
+ 63
+ 64 # Export Underscore to global scope.
+ 65 root._: _
+ 66 67
- 68
- 69 # Return the results of applying the iterator to each element. Use JavaScript
- 70 # 1.6's version of map, if possible.
- 71 _.map:(obj, iterator, context)->
- 72 return obj.map(iterator, context) if (obj and _.isFunction(obj.map))
- 73 results: []
- 74 _.each obj, (value, index, list)->
- 75 results.push(iterator.call(context, value, index, list))
- 76 results
- 77
- 78
- 79 # Reduce builds up a single result from a list of values. Also known as
- 80 # inject, or foldl. Uses JavaScript 1.8's version of reduce, if possible.
- 81 _.reduce:(obj, memo, iterator, context)->
- 82 return obj.reduce(_.bind(iterator, context), memo) if (obj and _.isFunction(obj.reduce))
- 83 _.each obj, (value, index, list)->
- 84 memo: iterator.call(context, memo, value, index, list)
- 85 memo
- 86
+ 68 # Current version.
+ 69 _.VERSION:'0.6.0'
+ 70
+ 71
+ 72 # ------------------------ Collection Functions: ---------------------------
+ 73
+ 74 # The cornerstone, an each implementation.
+ 75 # Handles objects implementing forEach, arrays, and raw objects.
+ 76 _.each:(obj, iterator, context)->
+ 77 try
+ 78 if nativeForEach and obj.forEach is nativeForEach
+ 79 obj.forEach iterator, context
+ 80 elseif _.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 # The right-associative version of reduce, also known as foldr. Uses
- 89 # JavaScript 1.8's version of reduceRight, if available.
- 90 _.reduceRight:(obj, memo, iterator, context)->
- 91 return obj.reduceRight(_.bind(iterator, context), memo) if (obj and _.isFunction(obj.reduceRight))
- 92 _.each _.clone(_.toArray(obj)).reverse(), (value, index)->
- 93 memo: iterator.call(context, memo, value, index, obj)
- 94 memo
- 95
- 96
- 97 # Return the first value which passes a truth test.
- 98 _.detect:(obj, iterator, context)->
- 99 result:null
- 100 _.each obj, (value, index, list)->
- 101 if iterator.call(context, value, index, list)
- 102 result: value
- 103 _.breakLoop()
- 104 result
- 105
+ 88
+ 89 # Return the results of applying the iterator to each element. Use JavaScript
+ 90 # 1.6's version of map, if possible.
+ 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 # Reduce builds up a single result from a list of values. Also known as
+ 100 # inject, or foldl. Uses JavaScript 1.8's version of reduce, if possible.
+ 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 # Return all the elements that pass a truth test. Use JavaScript 1.6's
- 108 # filter(), if it exists.
- 109 _.select:(obj, iterator, context)->
- 110 if obj and _.isFunction(obj.filter) thenreturn obj.filter(iterator, context)
- 111 results: []
- 112 _.each obj, (value, index, list)->
- 113 results.push(value) if iterator.call(context, value, index, list)
- 114 results
+ 107
+ 108 # The right-associative version of reduce, also known as foldr. Uses
+ 109 # JavaScript 1.8's version of reduceRight, if available.
+ 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 # Return all the elements for which a truth test fails.
- 118 _.reject:(obj, iterator, context)->
- 119 results: []
+ 117 # Return the first value which passes a truth test.
+ 118 _.detect:(obj, iterator, context)->
+ 119 result:null 120 _.each obj, (value, index, list)->
- 121 results.push(value) ifnot iterator.call(context, value, index, list)
- 122 results
- 123
- 124
- 125 # Determine whether all of the elements match a truth test. Delegate to
- 126 # JavaScript 1.6's every(), if it is present.
- 127 _.all:(obj, iterator, context)->
- 128 iterator ||= _.identity
- 129 return obj.every(iterator, context) if obj and _.isFunction(obj.every)
- 130 result:true
- 131 _.each obj, (value, index, list)->
- 132 _.breakLoop() unless (result: result and iterator.call(context, value, index, list))
- 133 result
- 134
+ 121 if iterator.call context, value, index, list
+ 122 result: value
+ 123 _.breakLoop()
+ 124 result
+ 125
+ 126
+ 127 # Return all the elements that pass a truth test. Use JavaScript 1.6's
+ 128 # filter(), if it exists.
+ 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 # Determine if at least one element in the object matches a truth test. Use
- 137 # JavaScript 1.6's some(), if it exists.
- 138 _.any:(obj, iterator, context)->
- 139 iterator ||= _.identity
- 140 return obj.some(iterator, context) if obj and _.isFunction(obj.some)
- 141 result:false
- 142 _.each obj, (value, index, list)->
- 143 _.breakLoop() if (result: iterator.call(context, value, index, list))
- 144 result
- 145
- 146
- 147 # Determine if a given value is included in the array or object,
- 148 # based on '==='.
- 149 _.include:(obj, target)->
- 150 return _.indexOf(obj, target) isnt-1if obj and _.isFunction(obj.indexOf)
- 151 for key, val of obj
- 152 returntrueif val is target
- 153 false
+ 136
+ 137 # Return all the elements for which a truth test fails.
+ 138 _.reject:(obj, iterator, context)->
+ 139 results: []
+ 140 _.each obj, (value, index, list)->
+ 141 results.push value ifnot iterator.call context, value, index, list
+ 142 results
+ 143
+ 144
+ 145 # Determine whether all of the elements match a truth test. Delegate to
+ 146 # JavaScript 1.6's every(), if it is present.
+ 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 # Invoke a method with arguments on every item in a collection.
- 157 _.invoke:(obj, method)->
- 158 args: _.rest(arguments, 2)
- 159 (if method then val[method] else val).apply(val, args) for val in obj
- 160
- 161
- 162 # Convenience version of a common use case of map: fetching a property.
- 163 _.pluck:(obj, key)->
- 164 _.map(obj, ((val)-> val[key]))
+ 156 # Determine if at least one element in the object matches a truth test. Use
+ 157 # JavaScript 1.6's some(), if it exists.
+ 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 # Return the maximum item or (item-based computation).
- 168 _.max:(obj, iterator, context)->
- 169 return Math.max.apply(Math, obj) ifnot iterator and _.isArray(obj)
- 170 result: {computed:-Infinity}
- 171 _.each obj, (value, index, list)->
- 172 computed:if iterator then iterator.call(context, value, index, list) else value
- 173 computed >= result.computed and (result: {value: value, computed: computed})
- 174 result.value
+ 167 # Determine if a given value is included in the array or object,
+ 168 # based on '==='.
+ 169 _.include:(obj, target)->
+ 170 return _.indexOf(obj, target) isnt-1if nativeIndexOf and obj.indexOf is nativeIndexOf
+ 171 for key, val of obj
+ 172 returntrueif val is target
+ 173 false
+ 174 175
- 176
- 177 # Return the minimum element (or element-based computation).
- 178 _.min:(obj, iterator, context)->
- 179 return Math.min.apply(Math, obj) ifnot iterator and _.isArray(obj)
- 180 result: {computed:Infinity}
- 181 _.each obj, (value, index, list)->
- 182 computed:if iterator then iterator.call(context, value, index, list) else value
- 183 computed < result.computed and (result: {value: value, computed: computed})
- 184 result.value
+ 176 # Invoke a method with arguments on every item in a collection.
+ 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 # Convenience version of a common use case of map: fetching a property.
+ 183 _.pluck:(obj, key)->
+ 184 _.map(obj, (val)-> val[key])
185 186
- 187 # Sort the object's values by a criteria produced by an iterator.
- 188 _.sortBy:(obj, iterator, context)->
- 189 _.pluck(((_.map obj, (value, index, list)->
- 190 {value: value, criteria: iterator.call(context, value, index, list)}
- 191 ).sort((left, right)->
- 192 a: left.criteria; b: right.criteria
- 193 if a < b then-1elseif a > b then1else0
- 194 )), 'value')
+ 187 # Return the maximum item or (item-based computation).
+ 188 _.max:(obj, iterator, context)->
+ 189 return Math.max.apply(Math, obj) ifnot 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 # Use a comparator function to figure out at what index an object should
- 198 # be inserted so as to maintain order. Uses binary search.
- 199 _.sortedIndex:(array, obj, iterator)->
- 200 iterator ||= _.identity
- 201 low:0; high: array.length
- 202 while low < high
- 203 mid: (low + high) >>1
- 204 if iterator(array[mid]) < iterator(obj) thenlow: mid +1elsehigh: mid
- 205 low
+ 197 # Return the minimum element (or element-based computation).
+ 198 _.min:(obj, iterator, context)->
+ 199 return Math.min.apply(Math, obj) ifnot 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 # Convert anything iterable into a real, live array.
- 209 _.toArray:(iterable)->
- 210 return [] if (!iterable)
- 211 return iterable.toArray() if (iterable.toArray)
- 212 return iterable if (_.isArray(iterable))
- 213 return slice.call(iterable) if (_.isArguments(iterable))
- 214 _.values(iterable)
+ 207 # Sort the object's values by a criterion produced by an iterator.
+ 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-1elseif a > b then1else0
+ 214 )), 'value')
215 216
- 217 # Return the number of elements in an object.
- 218 _.size:(obj)-> _.toArray(obj).length
- 219
- 220
- 221 # -------------------------- Array Functions: ------------------------------
- 222
- 223 # Get the first element of an array. Passing "n" will return the first N
- 224 # values in the array. Aliased as "head". The "guard" check allows it to work
- 225 # with _.map.
- 226 _.first:(array, n, guard)->
- 227 if n andnot guard then slice.call(array, 0, n) else array[0]
+ 217 # Use a comparator function to figure out at what index an object should
+ 218 # be inserted so as to maintain order. Uses binary search.
+ 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) thenlow: mid +1elsehigh: mid
+ 226 low
+ 227 228
- 229
- 230 # Returns everything but the first entry of the array. Aliased as "tail".
- 231 # Especially useful on the arguments object. Passing an "index" will return
- 232 # the rest of the values in the array from that index onward. The "guard"
- 233 # check allows it to work with _.map.
- 234 _.rest:(array, index, guard)->
- 235 slice.call(array, if _.isUndefined(index) or guard then1else index)
+ 229 # Convert anything iterable into a real, live array.
+ 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 # Get the last element of an array.
- 239 _.last:(array)-> array[array.length -1]
+ 238 # Return the number of elements in an object.
+ 239 _.size:(obj)-> _.toArray(obj).length
240 241
- 242 # Trim out all falsy values from an array.
- 243 _.compact:(array)-> item for item in array when item
- 244
- 245
- 246 # Return a completely flattened version of an array.
- 247 _.flatten:(array)->
- 248 _.reduce array, [], (memo, value)->
- 249 return memo.concat(_.flatten(value)) if _.isArray(value)
- 250 memo.push(value)
- 251 memo
- 252
- 253
- 254 # Return a version of the array that does not contain the specified value(s).
- 255 _.without:(array)->
- 256 values: _.rest(arguments)
- 257 val for val in _.toArray(array) whennot _.include(values, val)
+ 242 # -------------------------- Array Functions: ------------------------------
+ 243
+ 244 # Get the first element of an array. Passing "n" will return the first N
+ 245 # values in the array. Aliased as "head". The "guard" check allows it to work
+ 246 # with _.map.
+ 247 _.first:(array, n, guard)->
+ 248 if n andnot guard then slice.call(array, 0, n) else array[0]
+ 249
+ 250
+ 251 # Returns everything but the first entry of the array. Aliased as "tail".
+ 252 # Especially useful on the arguments object. Passing an "index" will return
+ 253 # the rest of the values in the array from that index onward. The "guard"
+ 254 # check allows it to work with _.map.
+ 255 _.rest:(array, index, guard)->
+ 256 slice.call(array, if _.isUndefined(index) or guard then1else index)
+ 257 258
- 259
- 260 # Produce a duplicate-free version of the array. If the array has already
- 261 # been sorted, you have the option of using a faster algorithm.
- 262 _.uniq:(array, isSorted)->
- 263 memo: []
- 264 for el, i in _.toArray(array)
- 265 memo.push(el) if i is0|| (if isSorted istruethen _.last(memo) isnt el elsenot _.include(memo, el))
- 266 memo
- 267
- 268
- 269 # Produce an array that contains every item shared between all the
- 270 # passed-in arrays.
- 271 _.intersect:(array)->
- 272 rest: _.rest(arguments)
- 273 _.select _.uniq(array), (item)->
- 274 _.all rest, (other)->
- 275 _.indexOf(other, item) >=0
- 276
- 277
- 278 # Zip together multiple lists into a single array -- elements that share
- 279 # an index go together.
- 280 _.zip:->
- 281 length: _.max(_.pluck(arguments, 'length'))
- 282 results:newArray(length)
- 283 for i in [0...length]
- 284 results[i]: _.pluck(arguments, String(i))
- 285 results
- 286
- 287
- 288 # If the browser doesn't supply us with indexOf (I'm looking at you, MSIE),
- 289 # we need this function. Return the position of the first occurence of an
- 290 # item in an array, or -1 if the item is not included in the array.
- 291 _.indexOf:(array, item)->
- 292 return array.indexOf(item) if array.indexOf
- 293 i:0; l: array.length
- 294 while l - i
- 295 if array[i] is item thenreturn i else i++
- 296 -1
+ 259 # Get the last element of an array.
+ 260 _.last:(array)-> array[array.length -1]
+ 261
+ 262
+ 263 # Trim out all falsy values from an array.
+ 264 _.compact:(array)-> item for item in array when item
+ 265
+ 266
+ 267 # Return a completely flattened version of an array.
+ 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 # Return a version of the array that does not contain the specified value(s).
+ 276 _.without:(array)->
+ 277 values: _.rest arguments
+ 278 val for val in _.toArray(array) whennot _.include values, val
+ 279
+ 280
+ 281 # Produce a duplicate-free version of the array. If the array has already
+ 282 # been sorted, you have the option of using a faster algorithm.
+ 283 _.uniq:(array, isSorted)->
+ 284 memo: []
+ 285 for el, i in _.toArray array
+ 286 memo.push el if i is0|| (if isSorted istruethen _.last(memo) isnt el elsenot _.include(memo, el))
+ 287 memo
+ 288
+ 289
+ 290 # Produce an array that contains every item shared between all the
+ 291 # passed-in arrays.
+ 292 _.intersect:(array)->
+ 293 rest: _.rest arguments
+ 294 _.select _.uniq(array), (item)->
+ 295 _.all rest, (other)->
+ 296 _.indexOf(other, item) >=0 297 298
- 299 # Provide JavaScript 1.6's lastIndexOf, delegating to the native function,
- 300 # if possible.
- 301 _.lastIndexOf:(array, item)->
- 302 return array.lastIndexOf(item) if array.lastIndexOf
- 303 i: array.length
- 304 while i
- 305 if array[i] is item thenreturn i else i--
- 306 -1
+ 299 # Zip together multiple lists into a single array -- elements that share
+ 300 # an index go together.
+ 301 _.zip:->
+ 302 length: _.max _.pluck arguments, 'length'
+ 303 results:newArray length
+ 304 for i in [0...length]
+ 305 results[i]: _.pluck arguments, String i
+ 306 results
307 308
- 309 # Generate an integer Array containing an arithmetic progression. A port of
- 310 # the native Python range() function. See:
- 311 # http://docs.python.org/library/functions.html#range
- 312 _.range:(start, stop, step)->
- 313 a: arguments
- 314 solo: a.length <=1
- 315 i:start:if solo then0else a[0]
- 316 stop:if solo then a[0] else a[1]
- 317 step: a[2] or1
- 318 len: Math.ceil((stop - start) / step)
- 319 return [] if len <=0
- 320 range:newArray(len)
- 321 idx:0
- 322 whiletrue
- 323 return range if (if step >0then i - stop else stop - i) >=0
- 324 range[idx]: i
- 325 idx++
- 326 i+= step
- 327
+ 309 # If the browser doesn't supply us with indexOf (I'm looking at you, MSIE),
+ 310 # we need this function. Return the position of the first occurence of an
+ 311 # item in an array, or -1 if the item is not included in the array.
+ 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 thenreturn i else i++
+ 317 -1
+ 318
+ 319
+ 320 # Provide JavaScript 1.6's lastIndexOf, delegating to the native function,
+ 321 # if possible.
+ 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 thenreturn i else i--
+ 327 -1 328
- 329 # ----------------------- Function Functions: -----------------------------
- 330
- 331 # Create a function bound to a given object (assigning 'this', and arguments,
- 332 # optionally). Binding with arguments is also known as 'curry'.
- 333 _.bind:(func, obj)->
- 334 args: _.rest(arguments, 2)
- 335 -> func.apply(obj or root, args.concat(arguments))
- 336
- 337
- 338 # Bind all of an object's methods to that object. Useful for ensuring that
- 339 # all callbacks defined on an object belong to it.
- 340 _.bindAll:(obj)->
- 341 funcs:if arguments.length >1then _.rest(arguments) else _.functions(obj)
- 342 _.each(funcs, (f)-> obj[f]: _.bind(obj[f], obj))
- 343 obj
- 344
- 345
- 346 # Delays a function for the given number of milliseconds, and then calls
- 347 # it with the arguments supplied.
- 348 _.delay:(func, wait)->
- 349 args: _.rest(arguments, 2)
- 350 setTimeout((-> func.apply(func, args)), wait)
+ 329
+ 330 # Generate an integer Array containing an arithmetic progression. A port of
+ 331 # the native Python range() function. See:
+ 332 # http://docs.python.org/library/functions.html#range
+ 333 _.range:(start, stop, step)->
+ 334 a: arguments
+ 335 solo: a.length <=1
+ 336 i:start:if solo then0else a[0]
+ 337 stop:if solo then a[0] else a[1]
+ 338 step: a[2] or1
+ 339 len: Math.ceil((stop - start) / step)
+ 340 return [] if len <=0
+ 341 range:newArray len
+ 342 idx:0
+ 343 whiletrue
+ 344 return range if (if step >0then i - stop else stop - i) >=0
+ 345 range[idx]: i
+ 346 idx++
+ 347 i+= step
+ 348
+ 349
+ 350 # ----------------------- Function Functions: ----------------------------- 351
- 352
- 353 # Defers a function, scheduling it to run after the current call stack has
- 354 # cleared.
- 355 _.defer:(func)->
- 356 _.delay.apply(_, [func, 1].concat(_.rest(arguments)))
+ 352 # Create a function bound to a given object (assigning 'this', and arguments,
+ 353 # optionally). Binding with arguments is also known as 'curry'.
+ 354 _.bind:(func, obj)->
+ 355 args: _.rest arguments, 2
+ 356 -> func.apply obj or root, args.concat arguments
357 358
- 359 # Returns the first function passed as an argument to the second,
- 360 # allowing you to adjust arguments, run code before and after, and
- 361 # conditionally execute the original function.
- 362 _.wrap:(func, wrapper)->
- 363 -> wrapper.apply(wrapper, [func].concat(arguments))
- 364
+ 359 # Bind all of an object's methods to that object. Useful for ensuring that
+ 360 # all callbacks defined on an object belong to it.
+ 361 _.bindAll:(obj)->
+ 362 funcs:if arguments.length >1then _.rest(arguments) else _.functions(obj)
+ 363 _.each funcs, (f)-> obj[f]: _.bind obj[f], obj
+ 364 obj
365
- 366 # Returns a function that is the composition of a list of functions, each
- 367 # consuming the return value of the function that follows.
- 368 _.compose:->
- 369 funcs: arguments
- 370 ->
- 371 args: arguments
- 372 for i in [(funcs.length -1)..0]
- 373 args: [funcs[i].apply(this, args)]
- 374 args[0]
- 375
- 376
- 377 # ------------------------- Object Functions: ----------------------------
+ 366
+ 367 # Delays a function for the given number of milliseconds, and then calls
+ 368 # it with the arguments supplied.
+ 369 _.delay:(func, wait)->
+ 370 args: _.rest arguments, 2
+ 371 setTimeout((-> func.apply(func, args)), wait)
+ 372
+ 373
+ 374 # Defers a function, scheduling it to run after the current call stack has
+ 375 # cleared.
+ 376 _.defer:(func)->
+ 377 _.delay.apply _, [func, 1].concat _.rest arguments
378
- 379 # Retrieve the names of an object's properties.
- 380 _.keys:(obj)->
- 381 return _.range(0, obj.length) if _.isArray(obj)
- 382 key for key, val of obj
- 383
- 384
- 385 # Retrieve the values of an object's properties.
- 386 _.values:(obj)->
- 387 _.map(obj, _.identity)
- 388
- 389
- 390 # Return a sorted list of the function names available in Underscore.
- 391 _.functions:(obj)->
- 392 _.select(_.keys(obj), (key)-> _.isFunction(obj[key])).sort()
- 393
- 394
- 395 # Extend a given object with all of the properties in a source object.
- 396 _.extend:(destination, source)->
- 397 for key, val of source
- 398 destination[key]: val
- 399 destination
- 400
- 401
- 402 # Create a (shallow-cloned) duplicate of an object.
- 403 _.clone:(obj)->
- 404 return obj.slice(0) if _.isArray(obj)
- 405 _.extend({}, obj)
- 406
- 407
- 408 # Invokes interceptor with the obj, and then returns obj.
- 409 # The primary purpose of this method is to "tap into" a method chain, in order to perform operations on intermediate results within the chain.
- 410 _.tap:(obj, interceptor)->
- 411 interceptor(obj)
- 412 obj
- 413
+ 379
+ 380 # Returns the first function passed as an argument to the second,
+ 381 # allowing you to adjust arguments, run code before and after, and
+ 382 # conditionally execute the original function.
+ 383 _.wrap:(func, wrapper)->
+ 384 -> wrapper.apply wrapper, [func].concat arguments
+ 385
+ 386
+ 387 # Returns a function that is the composition of a list of functions, each
+ 388 # consuming the return value of the function that follows.
+ 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 # ------------------------- Object Functions: ----------------------------
+ 399
+ 400 # Retrieve the names of an object's properties.
+ 401 _.keys: nativeKeys or(obj)->
+ 402 return _.range 0, obj.length if _.isArray(obj)
+ 403 key for key, val of obj
+ 404
+ 405
+ 406 # Retrieve the values of an object's properties.
+ 407 _.values:(obj)->
+ 408 _.map obj, _.identity
+ 409
+ 410
+ 411 # Return a sorted list of the function names available in Underscore.
+ 412 _.functions:(obj)->
+ 413 _.filter(_.keys(obj), (key)-> _.isFunction(obj[key])).sort()
414
- 415 # Perform a deep comparison to check if two objects are equal.
- 416 _.isEqual:(a, b)->
- 417 # Check object identity.
- 418 returntrueif a is b
- 419 # Different types?
- 420 atype:typeof(a); btype:typeof(b)
- 421 returnfalseif atype isnt btype
- 422 # Basic equality test (watch out for coercions).
- 423 returntrueif`a == b`
- 424 # One is falsy and the other truthy.
- 425 returnfalseif (!a and b) or (a and!b)
- 426 # One of them implements an isEqual()?
- 427 return a.isEqual(b) if a.isEqual
- 428 # Check dates' integer values.
- 429 return a.getTime() is b.getTime() if _.isDate(a) and _.isDate(b)
- 430 # Both are NaN?
- 431 returntrueif _.isNaN(a) and _.isNaN(b)
- 432 # Compare regular expressions.
- 433 if _.isRegExp(a) and _.isRegExp(b)
- 434 return a.source is b.source and
- 435 a.global is b.global and
- 436 a.ignoreCase is b.ignoreCase and
- 437 a.multiline is b.multiline
- 438 # If a is not an object by this point, we can't handle it.
- 439 returnfalseif atype isnt'object'
- 440 # Check for different array lengths before comparing contents.
- 441 returnfalseif a.length and (a.length isnt b.length)
- 442 # Nothing else worked, deep compare the contents.
- 443 aKeys: _.keys(a); bKeys: _.keys(b)
- 444 # Different object sizes?
- 445 returnfalseif aKeys.length isnt bKeys.length
- 446 # Recursive comparison of contents.
- 447 # for (var key in a) if (!_.isEqual(a[key], b[key])) return false;
- 448 returntrue
- 449
- 450
- 451 # Is a given array or object empty?
- 452 _.isEmpty:(obj)-> _.keys(obj).length is0
- 453
- 454
- 455 # Is a given value a DOM element?
- 456 _.isElement:(obj)-> obj and obj.nodeType is1
- 457
- 458
- 459 # Is a given value an array?
- 460 _.isArray:(obj)->!!(obj and obj.concat and obj.unshift)
- 461
- 462
- 463 # Is a given variable an arguments object?
- 464 _.isArguments:(obj)-> obj and _.isNumber(obj.length) andnot obj.concat and
- 465 not obj.substr andnot obj.apply andnot propertyIsEnumerable.call(obj, 'length')
- 466
- 467
- 468 # Is the given value a function?
- 469 _.isFunction:(obj)->!!(obj and obj.constructor and obj.call and obj.apply)
+ 415
+ 416 # Extend a given object with all of the properties in a source object.
+ 417 _.extend:(destination, source)->
+ 418 (destination[key]: val) for key, val of source
+ 419 destination
+ 420
+ 421
+ 422 # Create a (shallow-cloned) duplicate of an object.
+ 423 _.clone:(obj)->
+ 424 return obj.slice 0if _.isArray obj
+ 425 _.extend {}, obj
+ 426
+ 427
+ 428 # Invokes interceptor with the obj, and then returns obj.
+ 429 # The primary purpose of this method is to "tap into" a method chain, in order to perform operations on intermediate results within the chain.
+ 430 _.tap:(obj, interceptor)->
+ 431 interceptor obj
+ 432 obj
+ 433
+ 434
+ 435 # Perform a deep comparison to check if two objects are equal.
+ 436 _.isEqual:(a, b)->
+ 437 # Check object identity.
+ 438 returntrueif a is b
+ 439 # Different types?
+ 440 atype:typeof(a); btype:typeof(b)
+ 441 returnfalseif atype isnt btype
+ 442 # Basic equality test (watch out for coercions).
+ 443 returntrueif`a == b`
+ 444 # One is falsy and the other truthy.
+ 445 returnfalseif (!a and b) or (a and!b)
+ 446 # One of them implements an isEqual()?
+ 447 return a.isEqual(b) if a.isEqual
+ 448 # Check dates' integer values.
+ 449 return a.getTime() is b.getTime() if _.isDate(a) and _.isDate(b)
+ 450 # Both are NaN?
+ 451 returntrueif _.isNaN(a) and _.isNaN(b)
+ 452 # Compare regular expressions.
+ 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 # If a is not an object by this point, we can't handle it.
+ 459 returnfalseif atype isnt'object'
+ 460 # Check for different array lengths before comparing contents.
+ 461 returnfalseif a.length and (a.length isnt b.length)
+ 462 # Nothing else worked, deep compare the contents.
+ 463 aKeys: _.keys(a); bKeys: _.keys(b)
+ 464 # Different object sizes?
+ 465 returnfalseif aKeys.length isnt bKeys.length
+ 466 # Recursive comparison of contents.
+ 467 (returnfalse) for key, val of a when!_.isEqual(val, b[key])
+ 468 true
+ 469 470
- 471
- 472 # Is the given value a string?
- 473 _.isString:(obj)->!!(obj is''or (obj and obj.charCodeAt and obj.substr))
- 474
- 475
- 476 # Is a given value a number?
- 477 _.isNumber:(obj)-> (obj is+obj) or toString.call(obj) is'[object Number]'
- 478
- 479
- 480 # Is a given value a Date?
- 481 _.isDate:(obj)->!!(obj and obj.getTimezoneOffset and obj.setUTCFullYear)
- 482
- 483
- 484 # Is the given value a regular expression?
- 485 _.isRegExp:(obj)->!!(obj and obj.exec and (obj.ignoreCase or obj.ignoreCase isfalse))
- 486
- 487
- 488 # Is the given value NaN -- this one is interesting. NaN != NaN, and
- 489 # isNaN(undefined) == true, so we make sure it's a number first.
- 490 _.isNaN:(obj)-> _.isNumber(obj) and window.isNaN(obj)
- 491
- 492
- 493 # Is a given value equal to null?
- 494 _.isNull:(obj)-> obj isnull
- 495
- 496
- 497 # Is a given variable undefined?
- 498 _.isUndefined:(obj)->typeof obj is'undefined'
- 499
- 500
- 501 # -------------------------- Utility Functions: --------------------------
+ 471 # Is a given array or object empty?
+ 472 _.isEmpty:(obj)->
+ 473 return obj.length is0if _.isArray obj
+ 474 (returnfalse) for key of obj when hasOwnProperty.call(obj, key)
+ 475 true
+ 476
+ 477
+ 478 # Is a given value a DOM element?
+ 479 _.isElement:(obj)-> obj and obj.nodeType is1
+ 480
+ 481
+ 482 # Is a given value an array?
+ 483 _.isArray: nativeIsArray or(obj)->!!(obj and obj.concat and obj.unshift)
+ 484
+ 485
+ 486 # Is a given variable an arguments object?
+ 487 _.isArguments:(obj)-> obj and _.isNumber(obj.length) andnot obj.concat and
+ 488 not obj.substr andnot obj.apply andnot propertyIsEnumerable.call(obj, 'length')
+ 489
+ 490
+ 491 # Is the given value a function?
+ 492 _.isFunction:(obj)->!!(obj and obj.constructor and obj.call and obj.apply)
+ 493
+ 494
+ 495 # Is the given value a string?
+ 496 _.isString:(obj)->!!(obj is''or (obj and obj.charCodeAt and obj.substr))
+ 497
+ 498
+ 499 # Is a given value a number?
+ 500 _.isNumber:(obj)-> (obj is+obj) or toString.call(obj) is'[object Number]'
+ 501 502
- 503 # Run Underscore.js in noConflict mode, returning the '_' variable to its
- 504 # previous owner. Returns a reference to the Underscore object.
- 505 _.noConflict:->
- 506 root._: previousUnderscore
- 507 this
- 508
+ 503 # Is a given value a boolean?
+ 504 _.isBoolean:(obj)-> obj istrueor obj isfalse
+ 505
+ 506
+ 507 # Is a given value a Date?
+ 508 _.isDate:(obj)->!!(obj and obj.getTimezoneOffset and obj.setUTCFullYear)
509
- 510 # Keep the identity function around for default iterators.
- 511 _.identity:(value)-> value
- 512
+ 510
+ 511 # Is the given value a regular expression?
+ 512 _.isRegExp:(obj)->!!(obj and obj.exec and (obj.ignoreCase or obj.ignoreCase isfalse))
513
- 514 # Break out of the middle of an iteration.
- 515 _.breakLoop:->throw breaker
- 516
- 517
- 518 # Generate a unique integer id (unique within the entire client session).
- 519 # Useful for temporary DOM ids.
- 520 idCounter:0
- 521 _.uniqueId:(prefix)->
- 522 (prefix or'') + idCounter++
+ 514
+ 515 # Is the given value NaN -- this one is interesting. NaN != NaN, and
+ 516 # isNaN(undefined) == true, so we make sure it's a number first.
+ 517 _.isNaN:(obj)-> _.isNumber(obj) and window.isNaN(obj)
+ 518
+ 519
+ 520 # Is a given value equal to null?
+ 521 _.isNull:(obj)-> obj isnull
+ 522 523
- 524
- 525 # By default, Underscore uses ERB-style template delimiters, change the
- 526 # following template settings to use alternative delimiters.
- 527 _.templateSettings: {
- 528 start:'<%'
- 529 end:'%>'
- 530 interpolate:/<%=(.+?)%>/g
- 531 }
- 532
- 533
- 534 # JavaScript templating a-la ERB, pilfered from John Resig's
- 535 # "Secrets of the JavaScript Ninja", page 83.
- 536 # Single-quote fix from Rick Strahl's version.
- 537 _.template:(str, data)->
- 538 c: _.templateSettings
- 539 fn:newFunction'obj',
- 540 'var p=[],print=function(){p.push.apply(p,arguments);};'+
- 541 'with(obj){p.push(\''+
- 542 str.replace(/[\r\t\n]/g, "")
- 543 .replace(newRegExp("'(?=[^"+c.end[0]+"]*"+c.end+")","g"),"\t")
- 544 .split("'").join("\\'")
- 545 .split("\t").join("'")
- 546 .replace(c.interpolate, "',$1,'")
- 547 .split(c.start).join("');")
- 548 .split(c.end).join("p.push('") +
- 549 "');}return p.join('');"
- 550 if data then fn(data) else fn
- 551
- 552
- 553 # ------------------------------- Aliases ----------------------------------
- 554
- 555 _.forEach: _.each
- 556 _.foldl:_.inject: _.reduce
- 557 _.foldr: _.reduceRight
- 558 _.filter: _.select
- 559 _.every: _.all
- 560 _.some: _.any
- 561 _.head: _.first
- 562 _.tail: _.rest
- 563 _.methods: _.functions
- 564
- 565
- 566 # ------------------------ Setup the OOP Wrapper: --------------------------
- 567
- 568 # Helper function to continue chaining intermediate results.
- 569 result:(obj, chain)->
- 570 if chain then _(obj).chain() else obj
+ 524 # Is a given variable undefined?
+ 525 _.isUndefined:(obj)->typeof obj is'undefined'
+ 526
+ 527
+ 528 # -------------------------- Utility Functions: --------------------------
+ 529
+ 530 # Run Underscore.js in noConflict mode, returning the '_' variable to its
+ 531 # previous owner. Returns a reference to the Underscore object.
+ 532 _.noConflict:->
+ 533 root._: previousUnderscore
+ 534 this
+ 535
+ 536
+ 537 # Keep the identity function around for default iterators.
+ 538 _.identity:(value)-> value
+ 539
+ 540
+ 541 # Run a function n times.
+ 542 _.times:(n, iterator, context)->
+ 543 iterator.call(context, i) for i in [0...n]
+ 544
+ 545
+ 546 # Break out of the middle of an iteration.
+ 547 _.breakLoop:->throw breaker
+ 548
+ 549
+ 550 # Add your own custom functions to the Underscore object, ensuring that
+ 551 # they're correctly added to the OOP wrapper as well.
+ 552 _.mixin:(obj)->
+ 553 for name in _.functions(obj)
+ 554 addToWrapper name, _[name]: obj[name]
+ 555
+ 556
+ 557 # Generate a unique integer id (unique within the entire client session).
+ 558 # Useful for temporary DOM ids.
+ 559 idCounter:0
+ 560 _.uniqueId:(prefix)->
+ 561 (prefix or'') + idCounter++
+ 562
+ 563
+ 564 # By default, Underscore uses ERB-style template delimiters, change the
+ 565 # following template settings to use alternative delimiters.
+ 566 _.templateSettings: {
+ 567 start:'<%'
+ 568 end:'%>'
+ 569 interpolate:/<%=(.+?)%>/g
+ 570 }
571 572
- 573 # Add all of the Underscore functions to the wrapper object.
- 574 _.each _.functions(_), (name)->
- 575 method: _[name]
- 576 wrapper.prototype[name]:->
- 577 unshift.call(arguments, this._wrapped)
- 578 result(method.apply(_, arguments), this._chain)
- 579
- 580
- 581 # Add all mutator Array functions to the wrapper.
- 582 _.each ['pop', 'push', 'reverse', 'shift', 'sort', 'splice', 'unshift'], (name)->
- 583 method: Array.prototype[name]
- 584 wrapper.prototype[name]:->
- 585 method.apply(this._wrapped, arguments)
- 586 result(this._wrapped, this._chain)
- 587
- 588
- 589 # Add all accessor Array functions to the wrapper.
- 590 _.each ['concat', 'join', 'slice'], (name)->
- 591 method: Array.prototype[name]
- 592 wrapper.prototype[name]:->
- 593 result(method.apply(this._wrapped, arguments), this._chain)
+ 573 # JavaScript templating a-la ERB, pilfered from John Resig's
+ 574 # "Secrets of the JavaScript Ninja", page 83.
+ 575 # Single-quote fix from Rick Strahl's version.
+ 576 _.template:(str, data)->
+ 577 c: _.templateSettings
+ 578 endMatch:newRegExp("'(?=[^"+c.end.substr(0, 1)+"]*"+escapeRegExp(c.end)+")","g")
+ 579 fn:newFunction'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 # ------------------------------- Aliases ---------------------------------- 594
- 595
- 596 # Start chaining a wrapped Underscore object.
- 597 wrapper::chain:->
- 598 this._chain:true
- 599 this
- 600
- 601
- 602 # Extracts the result from a wrapped and chained object.
- 603 wrapper::value:->this._wrapped
-
+ 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 # ------------------------ Setup the OOP Wrapper: --------------------------
+ 607
+ 608 # If Underscore is called as a function, it returns a wrapped object that
+ 609 # can be used OO-style. This wrapper holds altered versions of all the
+ 610 # underscore functions. Wrapped objects may be chained.
+ 611 wrapper:(obj)->
+ 612 this._wrapped: obj
+ 613 this
+ 614
+ 615
+ 616 # Helper function to continue chaining intermediate results.
+ 617 result:(obj, chain)->
+ 618 if chain then _(obj).chain() else obj
+ 619
+ 620
+ 621 # A method to easily add functions to the OOP wrapper.
+ 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 # Add all of the Underscore functions to the wrapper object.
+ 630 _.mixin _
+ 631
+ 632
+ 633 # Add all mutator Array functions to the wrapper.
+ 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 # Add all accessor Array functions to the wrapper.
+ 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 # Start chaining a wrapped Underscore object.
+ 649 wrapper::chain:->
+ 650 this._chain:true
+ 651 this
+ 652
+ 653
+ 654 # Extracts the result from a wrapped and chained object.
+ 655 wrapper::value:->this._wrapped
+
diff --git a/examples/underscore.coffee b/examples/underscore.coffee
index 59970014..442c0335 100644
--- a/examples/underscore.coffee
+++ b/examples/underscore.coffee
@@ -18,36 +18,55 @@
previousUnderscore: root._
- # If Underscore is called as a function, it returns a wrapped object that
- # can be used OO-style. This wrapper holds altered versions of all the
- # underscore functions. Wrapped objects may be chained.
- wrapper: (obj) ->
- this._wrapped: obj
- this
-
-
# Establish the object that gets thrown to break out of a loop iteration.
breaker: if typeof(StopIteration) is 'undefined' then '__break__' else StopIteration
- # Create a safe reference to the Underscore object forreference below.
- _: root._: (obj) -> new wrapper(obj)
+ # Quick regexp-escaping function, because JS doesn't have RegExp.escape().
+ escapeRegExp: (string) -> string.replace(/([.*+?^${}()|[\]\/\\])/g, '\\$1')
+
+
+ # Save bytes in the minified (but not gzipped) version:
+ ArrayProto: Array.prototype
+ ObjProto: Object.prototype
+
+
+ #Create quick reference variables for speed access to core prototypes.
+ slice: ArrayProto.slice
+ unshift: ArrayProto.unshift
+ toString: ObjProto.toString
+ hasOwnProperty: ObjProto.hasOwnProperty
+ propertyIsEnumerable: ObjProto.propertyIsEnumerable
+
+
+ # All ECMA5 native implementations we hope to use are declared here.
+ nativeForEach: ArrayProto.forEach
+ nativeMap: ArrayProto.map
+ nativeReduce: ArrayProto.reduce
+ nativeReduceRight: ArrayProto.reduceRight
+ nativeFilter: ArrayProto.filter
+ nativeEvery: ArrayProto.every
+ nativeSome: ArrayProto.some
+ nativeIndexOf: ArrayProto.indexOf
+ nativeLastIndexOf: ArrayProto.lastIndexOf
+ nativeIsArray: Array.isArray
+ nativeKeys: Object.keys
+
+
+ # Create a safe reference to the Underscore object for use below.
+ _: (obj) -> new wrapper(obj)
# Export the Underscore object for CommonJS.
if typeof(exports) != 'undefined' then exports._: _
- # Create quick reference variables for speed access to core prototypes.
- slice: Array::slice
- unshift: Array::unshift
- toString: Object::toString
- hasOwnProperty: Object::hasOwnProperty
- propertyIsEnumerable: Object::propertyIsEnumerable
+ # Export Underscore to global scope.
+ root._: _
# Current version.
- _.VERSION: '0.5.8'
+ _.VERSION: '0.6.0'
# ------------------------ Collection Functions: ---------------------------
@@ -55,12 +74,13 @@
# The cornerstone, an each implementation.
# Handles objects implementing forEach, arrays, and raw objects.
_.each: (obj, iterator, context) ->
- index: 0
try
- return obj.forEach(iterator, context) if obj.forEach
- if _.isNumber(obj.length)
- return iterator.call(context, obj[i], i, obj) for i in [0...obj.length]
- iterator.call(context, val, key, obj) for key, val of obj
+ if nativeForEach and obj.forEach is nativeForEach
+ obj.forEach iterator, context
+ else if _.isNumber obj.length
+ iterator.call(context, obj[i], i, obj) for i in [0...obj.length]
+ else
+ iterator.call(context, val, key, obj) for key, val of obj
catch e
throw e if e isnt breaker
obj
@@ -69,28 +89,28 @@
# Return the results of applying the iterator to each element. Use JavaScript
# 1.6's version of map, if possible.
_.map: (obj, iterator, context) ->
- return obj.map(iterator, context) if (obj and _.isFunction(obj.map))
+ return obj.map(iterator, context) if nativeMap and obj.map is nativeMap
results: []
_.each obj, (value, index, list) ->
- results.push(iterator.call(context, value, index, list))
+ results.push iterator.call context, value, index, list
results
# Reduce builds up a single result from a list of values. Also known as
# inject, or foldl. Uses JavaScript 1.8's version of reduce, if possible.
_.reduce: (obj, memo, iterator, context) ->
- return obj.reduce(_.bind(iterator, context), memo) if (obj and _.isFunction(obj.reduce))
+ return obj.reduce(_.bind(iterator, context), memo) if nativeReduce and obj.reduce is nativeReduce
_.each obj, (value, index, list) ->
- memo: iterator.call(context, memo, value, index, list)
+ memo: iterator.call context, memo, value, index, list
memo
# The right-associative version of reduce, also known as foldr. Uses
# JavaScript 1.8's version of reduceRight, if available.
_.reduceRight: (obj, memo, iterator, context) ->
- return obj.reduceRight(_.bind(iterator, context), memo) if (obj and _.isFunction(obj.reduceRight))
+ return obj.reduceRight(_.bind(iterator, context), memo) if nativeReduceRight and obj.reduceRight is nativeReduceRight
_.each _.clone(_.toArray(obj)).reverse(), (value, index) ->
- memo: iterator.call(context, memo, value, index, obj)
+ memo: iterator.call context, memo, value, index, obj
memo
@@ -98,7 +118,7 @@
_.detect: (obj, iterator, context) ->
result: null
_.each obj, (value, index, list) ->
- if iterator.call(context, value, index, list)
+ if iterator.call context, value, index, list
result: value
_.breakLoop()
result
@@ -106,11 +126,11 @@
# Return all the elements that pass a truth test. Use JavaScript 1.6's
# filter(), if it exists.
- _.select: (obj, iterator, context) ->
- if obj and _.isFunction(obj.filter) then return obj.filter(iterator, context)
+ _.filter: (obj, iterator, context) ->
+ return obj.filter iterator, context if nativeFilter and obj.filter is nativeFilter
results: []
_.each obj, (value, index, list) ->
- results.push(value) if iterator.call(context, value, index, list)
+ results.push value if iterator.call context, value, index, list
results
@@ -118,15 +138,15 @@
_.reject: (obj, iterator, context) ->
results: []
_.each obj, (value, index, list) ->
- results.push(value) if not iterator.call(context, value, index, list)
+ results.push value if not iterator.call context, value, index, list
results
# Determine whether all of the elements match a truth test. Delegate to
# JavaScript 1.6's every(), if it is present.
- _.all: (obj, iterator, context) ->
+ _.every: (obj, iterator, context) ->
iterator ||= _.identity
- return obj.every(iterator, context) if obj and _.isFunction(obj.every)
+ return obj.every iterator, context if nativeEvery and obj.every is nativeEvery
result: true
_.each obj, (value, index, list) ->
_.breakLoop() unless (result: result and iterator.call(context, value, index, list))
@@ -135,9 +155,9 @@
# Determine if at least one element in the object matches a truth test. Use
# JavaScript 1.6's some(), if it exists.
- _.any: (obj, iterator, context) ->
+ _.some: (obj, iterator, context) ->
iterator ||= _.identity
- return obj.some(iterator, context) if obj and _.isFunction(obj.some)
+ return obj.some iterator, context if nativeSome and obj.some is nativeSome
result: false
_.each obj, (value, index, list) ->
_.breakLoop() if (result: iterator.call(context, value, index, list))
@@ -147,7 +167,7 @@
# Determine if a given value is included in the array or object,
# based on '==='.
_.include: (obj, target) ->
- return _.indexOf(obj, target) isnt -1 if obj and _.isFunction(obj.indexOf)
+ return _.indexOf(obj, target) isnt -1 if nativeIndexOf and obj.indexOf is nativeIndexOf
for key, val of obj
return true if val is target
false
@@ -155,13 +175,13 @@
# Invoke a method with arguments on every item in a collection.
_.invoke: (obj, method) ->
- args: _.rest(arguments, 2)
+ args: _.rest arguments, 2
(if method then val[method] else val).apply(val, args) for val in obj
# Convenience version of a common use case of map: fetching a property.
_.pluck: (obj, key) ->
- _.map(obj, ((val) -> val[key]))
+ _.map(obj, (val) -> val[key])
# Return the maximum item or (item-based computation).
@@ -184,7 +204,7 @@
result.value
- # Sort the object's values by a criteria produced by an iterator.
+ # Sort the object's values by a criterion produced by an iterator.
_.sortBy: (obj, iterator, context) ->
_.pluck(((_.map obj, (value, index, list) ->
{value: value, criteria: iterator.call(context, value, index, list)}
@@ -198,7 +218,8 @@
# be inserted so as to maintain order. Uses binary search.
_.sortedIndex: (array, obj, iterator) ->
iterator ||= _.identity
- low: 0; high: array.length
+ low: 0
+ high: array.length
while low < high
mid: (low + high) >> 1
if iterator(array[mid]) < iterator(obj) then low: mid + 1 else high: mid
@@ -246,30 +267,30 @@
# Return a completely flattened version of an array.
_.flatten: (array) ->
_.reduce array, [], (memo, value) ->
- return memo.concat(_.flatten(value)) if _.isArray(value)
- memo.push(value)
+ return memo.concat(_.flatten(value)) if _.isArray value
+ memo.push value
memo
# Return a version of the array that does not contain the specified value(s).
_.without: (array) ->
- values: _.rest(arguments)
- val for val in _.toArray(array) when not _.include(values, val)
+ values: _.rest arguments
+ val for val in _.toArray(array) when not _.include values, val
# Produce a duplicate-free version of the array. If the array has already
# been sorted, you have the option of using a faster algorithm.
_.uniq: (array, isSorted) ->
memo: []
- for el, i in _.toArray(array)
- memo.push(el) if i is 0 || (if isSorted is true then _.last(memo) isnt el else not _.include(memo, el))
+ for el, i in _.toArray array
+ memo.push el if i is 0 || (if isSorted is true then _.last(memo) isnt el else not _.include(memo, el))
memo
# Produce an array that contains every item shared between all the
# passed-in arrays.
_.intersect: (array) ->
- rest: _.rest(arguments)
+ rest: _.rest arguments
_.select _.uniq(array), (item) ->
_.all rest, (other) ->
_.indexOf(other, item) >= 0
@@ -278,10 +299,10 @@
# Zip together multiple lists into a single array -- elements that share
# an index go together.
_.zip: ->
- length: _.max(_.pluck(arguments, 'length'))
- results: new Array(length)
+ length: _.max _.pluck arguments, 'length'
+ results: new Array length
for i in [0...length]
- results[i]: _.pluck(arguments, String(i))
+ results[i]: _.pluck arguments, String i
results
@@ -289,7 +310,7 @@
# we need this function. Return the position of the first occurence of an
# item in an array, or -1 if the item is not included in the array.
_.indexOf: (array, item) ->
- return array.indexOf(item) if array.indexOf
+ return array.indexOf item if nativeIndexOf and array.indexOf is nativeIndexOf
i: 0; l: array.length
while l - i
if array[i] is item then return i else i++
@@ -299,7 +320,7 @@
# Provide JavaScript 1.6's lastIndexOf, delegating to the native function,
# if possible.
_.lastIndexOf: (array, item) ->
- return array.lastIndexOf(item) if array.lastIndexOf
+ return array.lastIndexOf(item) if nativeLastIndexOf and array.lastIndexOf is nativeLastIndexOf
i: array.length
while i
if array[i] is item then return i else i--
@@ -317,7 +338,7 @@
step: a[2] or 1
len: Math.ceil((stop - start) / step)
return [] if len <= 0
- range: new Array(len)
+ range: new Array len
idx: 0
while true
return range if (if step > 0 then i - stop else stop - i) >= 0
@@ -331,36 +352,36 @@
# Create a function bound to a given object (assigning 'this', and arguments,
# optionally). Binding with arguments is also known as 'curry'.
_.bind: (func, obj) ->
- args: _.rest(arguments, 2)
- -> func.apply(obj or root, args.concat(arguments))
+ args: _.rest arguments, 2
+ -> func.apply obj or root, args.concat arguments
# Bind all of an object's methods to that object. Useful for ensuring that
# all callbacks defined on an object belong to it.
_.bindAll: (obj) ->
funcs: if arguments.length > 1 then _.rest(arguments) else _.functions(obj)
- _.each(funcs, (f) -> obj[f]: _.bind(obj[f], obj))
+ _.each funcs, (f) -> obj[f]: _.bind obj[f], obj
obj
# Delays a function for the given number of milliseconds, and then calls
# it with the arguments supplied.
_.delay: (func, wait) ->
- args: _.rest(arguments, 2)
+ args: _.rest arguments, 2
setTimeout((-> func.apply(func, args)), wait)
# Defers a function, scheduling it to run after the current call stack has
# cleared.
_.defer: (func) ->
- _.delay.apply(_, [func, 1].concat(_.rest(arguments)))
+ _.delay.apply _, [func, 1].concat _.rest arguments
# Returns the first function passed as an argument to the second,
# allowing you to adjust arguments, run code before and after, and
# conditionally execute the original function.
_.wrap: (func, wrapper) ->
- -> wrapper.apply(wrapper, [func].concat(arguments))
+ -> wrapper.apply wrapper, [func].concat arguments
# Returns a function that is the composition of a list of functions, each
@@ -377,38 +398,37 @@
# ------------------------- Object Functions: ----------------------------
# Retrieve the names of an object's properties.
- _.keys: (obj) ->
- return _.range(0, obj.length) if _.isArray(obj)
+ _.keys: nativeKeys or (obj) ->
+ return _.range 0, obj.length if _.isArray(obj)
key for key, val of obj
# Retrieve the values of an object's properties.
_.values: (obj) ->
- _.map(obj, _.identity)
+ _.map obj, _.identity
# Return a sorted list of the function names available in Underscore.
_.functions: (obj) ->
- _.select(_.keys(obj), (key) -> _.isFunction(obj[key])).sort()
+ _.filter(_.keys(obj), (key) -> _.isFunction(obj[key])).sort()
# Extend a given object with all of the properties in a source object.
_.extend: (destination, source) ->
- for key, val of source
- destination[key]: val
+ (destination[key]: val) for key, val of source
destination
# Create a (shallow-cloned) duplicate of an object.
_.clone: (obj) ->
- return obj.slice(0) if _.isArray(obj)
- _.extend({}, obj)
+ return obj.slice 0 if _.isArray obj
+ _.extend {}, obj
# Invokes interceptor with the obj, and then returns obj.
# The primary purpose of this method is to "tap into" a method chain, in order to perform operations on intermediate results within the chain.
_.tap: (obj, interceptor) ->
- interceptor(obj)
+ interceptor obj
obj
@@ -444,12 +464,15 @@
# Different object sizes?
return false if aKeys.length isnt bKeys.length
# Recursive comparison of contents.
- # for (var key in a) if (!_.isEqual(a[key], b[key])) return false;
- return true
+ (return false) for key, val of a when !_.isEqual(val, b[key])
+ true
# Is a given array or object empty?
- _.isEmpty: (obj) -> _.keys(obj).length is 0
+ _.isEmpty: (obj) ->
+ return obj.length is 0 if _.isArray obj
+ (return false) for key of obj when hasOwnProperty.call(obj, key)
+ true
# Is a given value a DOM element?
@@ -457,7 +480,7 @@
# Is a given value an array?
- _.isArray: (obj) -> !!(obj and obj.concat and obj.unshift)
+ _.isArray: nativeIsArray or (obj) -> !!(obj and obj.concat and obj.unshift)
# Is a given variable an arguments object?
@@ -477,6 +500,10 @@
_.isNumber: (obj) -> (obj is +obj) or toString.call(obj) is '[object Number]'
+ # Is a given value a boolean?
+ _.isBoolean: (obj) -> obj is true or obj is false
+
+
# Is a given value a Date?
_.isDate: (obj) -> !!(obj and obj.getTimezoneOffset and obj.setUTCFullYear)
@@ -511,10 +538,22 @@
_.identity: (value) -> value
+ # Run a function n times.
+ _.times: (n, iterator, context) ->
+ iterator.call(context, i) for i in [0...n]
+
+
# Break out of the middle of an iteration.
_.breakLoop: -> throw breaker
+ # Add your own custom functions to the Underscore object, ensuring that
+ # they're correctly added to the OOP wrapper as well.
+ _.mixin: (obj) ->
+ for name in _.functions(obj)
+ addToWrapper name, _[name]: obj[name]
+
+
# Generate a unique integer id (unique within the entire client session).
# Useful for temporary DOM ids.
idCounter: 0
@@ -536,11 +575,12 @@
# Single-quote fix from Rick Strahl's version.
_.template: (str, data) ->
c: _.templateSettings
+ endMatch: new RegExp("'(?=[^"+c.end.substr(0, 1)+"]*"+escapeRegExp(c.end)+")","g")
fn: new Function 'obj',
'var p=[],print=function(){p.push.apply(p,arguments);};' +
'with(obj){p.push(\'' +
str.replace(/[\r\t\n]/g, " ")
- .replace(new RegExp("'(?=[^"+c.end[0]+"]*"+c.end+")","g"),"\t")
+ .replace(endMatch,"\t")
.split("'").join("\\'")
.split("\t").join("'")
.replace(c.interpolate, "',$1,'")
@@ -555,9 +595,9 @@
_.forEach: _.each
_.foldl: _.inject: _.reduce
_.foldr: _.reduceRight
- _.filter: _.select
- _.every: _.all
- _.some: _.any
+ _.select: _.filter
+ _.all: _.every
+ _.any: _.some
_.head: _.first
_.tail: _.rest
_.methods: _.functions
@@ -565,17 +605,29 @@
# ------------------------ Setup the OOP Wrapper: --------------------------
+ # If Underscore is called as a function, it returns a wrapped object that
+ # can be used OO-style. This wrapper holds altered versions of all the
+ # underscore functions. Wrapped objects may be chained.
+ wrapper: (obj) ->
+ this._wrapped: obj
+ this
+
+
# Helper function to continue chaining intermediate results.
result: (obj, chain) ->
if chain then _(obj).chain() else obj
- # Add all of the Underscore functions to the wrapper object.
- _.each _.functions(_), (name) ->
- method: _[name]
+ # A method to easily add functions to the OOP wrapper.
+ addToWrapper: (name, func) ->
wrapper.prototype[name]: ->
- unshift.call(arguments, this._wrapped)
- result(method.apply(_, arguments), this._chain)
+ args: _.toArray arguments
+ unshift.call args, this._wrapped
+ result func.apply(_, args), this._chain
+
+
+ # Add all of the Underscore functions to the wrapper object.
+ _.mixin _
# Add all mutator Array functions to the wrapper.