1  
   2    # Underscore.coffee
   3    # (c) 2010 Jeremy Ashkenas, DocumentCloud Inc.
   4    # Underscore is freely distributable under the terms of the MIT license.
   5    # Portions of Underscore are inspired by or borrowed from Prototype.js,
   6    # Oliver Steele's Functional, and John Resig's Micro-Templating.
   7    # For all details and documentation:
   8    # http://documentcloud.github.com/underscore/
   9  
  10  
  11    # ------------------------- Baseline setup ---------------------------------
  12  
  13    # Establish the root object, "window" in the browser, or "global" on the server.
  14    root: this
  15  
  16  
  17    # Save the previous value of the "_" variable.
  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
  27  
  28  
  29    # Establish the object that gets thrown to break out of a loop iteration.
  30    breaker: if typeof(StopIteration) is 'undefined' then '__break__' else StopIteration
  31  
  32  
  33    # Create a safe reference to the Underscore object forreference below.
  34    _: root._: (obj) -> new wrapper(obj)
  35  
  36  
  37    # Export the Underscore object for CommonJS.
  38    if typeof(exports) != 'undefined' then exports._: _
  39  
  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: ---------------------------
  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
  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  
  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  
 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) then return 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
 115  
 116  
 117    # Return all the elements for which a truth test fails.
 118    _.reject: (obj, iterator, context) ->
 119      results: []
 120      _.each obj, (value, index, list) ->
 121        results.push(value) if not 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  
 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 -1 if obj and _.isFunction(obj.indexOf)
 151      for key, val of obj
 152        return true if val is target
 153      false
 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]))
 165  
 166  
 167    # Return the maximum item or (item-based computation).
 168    _.max: (obj, iterator, context) ->
 169      return Math.max.apply(Math, obj) if not 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
 175  
 176  
 177    # Return the minimum element (or element-based computation).
 178    _.min: (obj, iterator, context) ->
 179      return Math.min.apply(Math, obj) if not 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
 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 -1 else if a > b then 1 else 0
 194      )), '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) then low: mid + 1 else high: mid
 205      low
 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)
 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 and not guard then slice.call(array, 0, n) else array[0]
 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 then 1 else index)
 236  
 237  
 238    # Get the last element of an array.
 239    _.last: (array) -> array[array.length - 1]
 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) when not _.include(values, val)
 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 is 0 || (if isSorted is true then _.last(memo) isnt el else not _.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:    new Array(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 then return i else i++
 296      -1
 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 then return i else i--
 306      -1
 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 then 0 else a[0]
 316      stop:     if solo then a[0] else a[1]
 317      step:     a[2] or 1
 318      len:      Math.ceil((stop - start) / step)
 319      return [] if len <= 0
 320      range:    new Array(len)
 321      idx:      0
 322      while true
 323        return range if (if step > 0 then i - stop else stop - i) >= 0
 324        range[idx]: i
 325        idx++
 326        i+= step
 327  
 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 > 1 then _.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)
 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)))
 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  
 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: ----------------------------
 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  
 414  
 415    # Perform a deep comparison to check if two objects are equal.
 416    _.isEqual: (a, b) ->
 417      # Check object identity.
 418      return true if a is b
 419      # Different types?
 420      atype: typeof(a); btype: typeof(b)
 421      return false if atype isnt btype
 422      # Basic equality test (watch out for coercions).
 423      return true if `a == b`
 424      # One is falsy and the other truthy.
 425      return false if (!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      return true if _.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      return false if atype isnt 'object'
 440      # Check for different array lengths before comparing contents.
 441      return false if 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      return false if 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      return true
 449  
 450  
 451    # Is a given array or object empty?
 452    _.isEmpty:      (obj) -> _.keys(obj).length is 0
 453  
 454  
 455    # Is a given value a DOM element?
 456    _.isElement:    (obj) -> obj and obj.nodeType is 1
 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) and not obj.concat and
 465                             not obj.substr and not obj.apply and not 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)
 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 is false))
 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 is null
 495  
 496  
 497    # Is a given variable undefined?
 498    _.isUndefined:  (obj) -> typeof obj is 'undefined'
 499  
 500  
 501    # -------------------------- Utility Functions: --------------------------
 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  
 509  
 510    # Keep the identity function around for default iterators.
 511    _.identity: (value) -> value
 512  
 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++
 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: new Function '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(new RegExp("'(?=[^"+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
 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)
 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

Valid XHTML 1.0 Strict Valid CSS!