Skip to content
This repository
Browse code

first underscore.js commit -- pulled out from documentcloud's core.js

  • Loading branch information...
commit 02ede85b539a89a44a71ce098f09a9553a3a6890 0 parents
Jeremy Ashkenas authored October 25, 2009

Showing 1 changed file with 368 additions and 0 deletions. Show diff stats Hide diff stats

  1. 368  underscore.js
368  underscore.js
... ...
@@ -0,0 +1,368 @@
  1
+// Javascript can be so much more pleasant when it's functional -- re-implement
  2
+// a bunch of utility methods from Prototype and Steele's Functional...
  3
+window._ = {
  4
+      
  5
+  // The cornerstone, an each implementation.
  6
+  // Handles objects implementing forEach, _each, arrays, and raw objects.
  7
+  each : function(obj, iterator, context) {
  8
+    var index = 0;
  9
+    try {
  10
+      if (obj.forEach) {
  11
+        obj.forEach(iterator, context);
  12
+      } else if (obj.length) {
  13
+        for (var i=0; i<obj.length; i++) {
  14
+          iterator.call(context, obj[i], i);
  15
+        }
  16
+      } else if (obj._each) {
  17
+        obj._each(function(value) {
  18
+          iterator.call(context, value, index++);
  19
+        });
  20
+      } else {
  21
+        var i = 0;
  22
+        for (var key in obj) {
  23
+          var value = obj[key], pair = [key, value];
  24
+          pair.key = key;
  25
+          pair.value = value;
  26
+          iterator.call(context, pair, i++);
  27
+        }
  28
+      }
  29
+    } catch(e) {
  30
+      if (e != '__break__') throw e;
  31
+    }
  32
+    return obj;
  33
+  },
  34
+  
  35
+  // Determine whether all of the elements match a truth test. Delegate to
  36
+  // Javascript 1.6's every(), if it is present.
  37
+  all : function(obj, iterator, context) {
  38
+    if (obj.every) return obj.every(iterator, context);
  39
+    var result = true;
  40
+    _.each(obj, function(value, index) {
  41
+      result = result && !!iterator.call(context, value, index);
  42
+      if (!result) throw '__break__';
  43
+    });
  44
+    return result;
  45
+  },
  46
+  
  47
+  // Determine if at least one element in the object matches a truth test. Use
  48
+  // Javascript 1.6's some(), if it exists.
  49
+  any : function(obj, iterator, context) {
  50
+    if (obj.some) return obj.some(iterator, context);
  51
+    var result = false;
  52
+    _.each(obj, function(value, index) {
  53
+      if (result = !!iterator.call(context, value, index)) throw '__break__';
  54
+    });
  55
+    return result;
  56
+  },
  57
+  
  58
+  // Return the results of applying the iterator to each element. Use Javascript
  59
+  // 1.6's version of map, if possible.
  60
+  map : function(obj, iterator, context) {
  61
+    if (obj.map) return obj.map(iterator, context);
  62
+    var results = [];
  63
+    _.each(obj, function(value, index) {
  64
+      results.push(iterator.call(context, value, index));
  65
+    });
  66
+    return results;
  67
+  },
  68
+  
  69
+  // Return the first value which passes a truth test.
  70
+  detect : function(obj, iterator, context) {
  71
+    var result;
  72
+    _.each(obj, function(value, index) {
  73
+      if (iterator.call(context, value, index)) {
  74
+        result = value;
  75
+        throw '__break__';
  76
+      }
  77
+    });
  78
+    return result;
  79
+  },
  80
+  
  81
+  // Return all the elements that pass a truth test. Use Javascript 1.6's
  82
+  // filter(), if it exists.
  83
+  select : function(obj, iterator, context) {
  84
+    if (obj.filter) return obj.filter(iterator, context);
  85
+    var results = [];
  86
+    _.each(obj, function(value, index) {
  87
+      if (iterator.call(context, value, index)) results.push(value);
  88
+    });
  89
+    return results;
  90
+  },
  91
+  
  92
+  // Determine if a given value is included in the object, based on '=='.
  93
+  include : function(obj, target) {
  94
+    if (_.isArray(obj)) if (obj.indexOf(target) != -1) return true;
  95
+    var found = false;
  96
+    _.each(obj, function(value) {
  97
+      if (value == target) {
  98
+        found = true;
  99
+        throw '__break__';
  100
+      }
  101
+    });
  102
+    return found;
  103
+  },
  104
+  
  105
+  // Aka reduce. Inject builds up a single result from a list of values.
  106
+  inject : function(obj, memo, iterator, context) {
  107
+    _.each(obj, function(value, index) {
  108
+      memo = iterator.call(context, memo, value, index);
  109
+    });
  110
+    return memo;
  111
+  },
  112
+  
  113
+  // Invoke a method with arguments on every item in a collection.
  114
+  invoke : function(obj, method) {
  115
+    var args = _.toArray(arguments).slice(2);
  116
+    return _.map(obj, function(value) {
  117
+      return (method ? value[method] : value).apply(value, args);
  118
+    });
  119
+  },
  120
+  
  121
+  // Return the maximum item or (item-based computation).
  122
+  max : function(obj, iterator, context) {
  123
+    var result;
  124
+    _.each(obj, function(value, index) {
  125
+      value = iterator ? iterator.call(context, value, index) : value;
  126
+      if (result == null || value >= result) result = value;
  127
+    });
  128
+    return result;
  129
+  },
  130
+  
  131
+  // Return the minimum element (or element-based computation).
  132
+  min : function(obj, iterator, context) {
  133
+    var result;
  134
+    _.each(obj, function(value, index) {
  135
+      value = iterator ? iterator.call(context, value, index) : value;
  136
+      if (result == null || value < result) result = value;
  137
+    });
  138
+    return result;
  139
+  },
  140
+  
  141
+  // Optimized version of a common use case of map: fetching a property.
  142
+  pluck : function(obj, key) {
  143
+    var results = [];
  144
+    _.each(obj, function(value){ results.push(value[key]); });
  145
+    return results;
  146
+  },
  147
+  
  148
+  // Return all the elements for which a truth test fails.
  149
+  reject : function(obj, iterator, context) {
  150
+    var results = [];
  151
+    _.each(obj, function(value, index) {
  152
+      if (!iterator.call(context, value, index)) results.push(value);
  153
+    });
  154
+    return results;
  155
+  },
  156
+  
  157
+  // Sort the object's values by a criteria produced by an iterator.
  158
+  sortBy : function(obj, iterator, context) {
  159
+    return _.pluck(_.map(obj, function(value, index) {
  160
+      return {
  161
+        value : value,
  162
+        criteria : iterator.call(context, value, index)
  163
+      };
  164
+    }).sort(function(left, right) {
  165
+      var a = left.criteria, b = right.criteria;
  166
+      return a < b ? -1 : a > b ? 1 : 0;
  167
+    }), 'value');
  168
+  },
  169
+  
  170
+  // Use a comparator function to figure out at what index an object should
  171
+  // be inserted so as to maintain order. Uses binary search.
  172
+  sortedIndex : function(array, comparator, obj) {
  173
+    var low = 0, high = array.length;
  174
+    while (low < high) {
  175
+      var mid = (low + high) >> 1;
  176
+      comparator(array[mid], obj) < 0 ? low = mid + 1 : high = mid;
  177
+    }
  178
+    return low;
  179
+  },
  180
+  
  181
+  // Convert anything iterable into a real, live array.
  182
+  toArray : function(iterable) {
  183
+    if (!iterable) return [];
  184
+    if (_.isArray(iterable)) return iterable;
  185
+    return _.map(iterable, function(val){ return val; });
  186
+  },
  187
+  
  188
+  // Return the number of elements in an object.
  189
+  size : function(obj) {
  190
+    _.toArray(obj).length;
  191
+  },
  192
+  
  193
+  //------------- The following methods only apply to arrays. -----------------
  194
+  
  195
+  // Get the first element of an array.
  196
+  first : function(array) {
  197
+    return array[0];
  198
+  },
  199
+  
  200
+  // Get the last element of an array.
  201
+  last : function(array) {
  202
+    return array[array.length - 1];
  203
+  },
  204
+  
  205
+  // Trim out all falsy values from an array.
  206
+  compact : function(array) {
  207
+    return _.select(array, function(value){ return !!value; });
  208
+  },
  209
+  
  210
+  // Return a completely flattened version of an array.
  211
+  flatten : function(array) {
  212
+    return _.inject(array, [], function(memo, value) {
  213
+      if (_.isArray(value)) return memo.concat(_.flatten(value));
  214
+      memo.push(value);
  215
+      return memo;
  216
+    });
  217
+  },
  218
+  
  219
+  // Return a version of the array that does not contain the specified value.
  220
+  without : function(array) {
  221
+    var values = array.slice.call(arguments, 0);
  222
+    return _.select(function(value){ return !_.include(values, value); });
  223
+  },
  224
+  
  225
+  // Produce a duplicate-free version of the array. If the array has already
  226
+  // been sorted, you have the option of using a faster algorithm.
  227
+  uniq : function(array, sorted) {
  228
+    return _.inject(array, [], function(memo, el, i) {
  229
+      if (0 == i || (sorted ? _.last(memo) != el : !_.include(memo, el))) memo.push(el);
  230
+      return memo;
  231
+    });
  232
+  },
  233
+  
  234
+  // Produce an array that contains every item shared between two given arrays.
  235
+  intersect : function(array1, array2) {
  236
+    return _.select(_.uniq(array1), function(item1) {
  237
+      return _.detect(array2, function(item2) { return item1 === item2; });
  238
+    });
  239
+  },
  240
+  
  241
+  // If the browser doesn't supply us with indexOf, we might need this function.
  242
+  // Return the position of the first occurence of an item in an array,
  243
+  // or -1 if the item is not included in the array.
  244
+  // indexOf : function(array, item) {
  245
+  //   var length = array.length;
  246
+  //   for (i=0; i<length; i++) if (array[i] === item) return i;
  247
+  //   return -1;
  248
+  // }
  249
+  
  250
+  /* ---------------- The following methods apply to objects ---------------- */
  251
+  
  252
+  // Retrieve the names of an object's properties.
  253
+  keys : function(obj) {
  254
+    return _.pluck(obj, 'key');
  255
+  },
  256
+  
  257
+  // Retrieve the values of an object's properties.
  258
+  values : function(obj) {
  259
+    return _.pluck(obj, 'value');
  260
+  },
  261
+  
  262
+  // Extend a given object with all of the properties in a source object.
  263
+  extend : function(destination, source) {
  264
+    for (var property in source) destination[property] = source[property];
  265
+    return destination;
  266
+  },
  267
+  
  268
+  // Create a (shallow-cloned) duplicate of an object.
  269
+  clone : function(obj) {
  270
+    return _.extend({}, obj);
  271
+  },
  272
+  
  273
+  // Is a given value a DOM element?
  274
+  isElement : function(obj) {
  275
+    return !!(obj && obj.nodeType == 1);
  276
+  },
  277
+  
  278
+  // Is a given value a real Array?
  279
+  isArray : function(obj) {
  280
+    return Object.prototype.toString.call(obj) == '[object Array]';
  281
+  },
  282
+  
  283
+  // Is a given value a Function?
  284
+  isFunction : function(obj) {
  285
+    return typeof obj == 'function';
  286
+  },
  287
+  
  288
+  // Is a given variable undefined?
  289
+  isUndefined : function(obj) {
  290
+    return typeof obj == 'undefined';
  291
+  },
  292
+  
  293
+  // Convert any value into printable string form.
  294
+  toString : function(obj) {
  295
+    return obj == null ? '' : String(obj);
  296
+  },
  297
+  
  298
+  // Create a function bound to a given object (assigning 'this', and arguments,
  299
+  // optionally).
  300
+  bind : function(func, context) {
  301
+    if (!context) return func;
  302
+    var args = _.toArray(arguments).slice(2);
  303
+    return function() {
  304
+      var a = args.concat(_.toArray(arguments));
  305
+      return func.apply(context, a);
  306
+    };
  307
+  },
  308
+  
  309
+  // Bind all of an object's methods to that object. Useful for ensuring that 
  310
+  // all callbacks defined on an object belong to it.
  311
+  bindAll : function() {
  312
+    var args = _.toArray(arguments);
  313
+    var context = args.pop();
  314
+    _.each(args, function(methodName) {
  315
+      context[methodName] = _.bind(context[methodName], context);
  316
+    });
  317
+  },
  318
+  
  319
+  // Generate a unique integer id (unique within the entire client session).
  320
+  // Useful for temporary DOM ids.
  321
+  uniqueId : function(prefix) {
  322
+    var id = this._idCounter = (this._idCounter || 0) + 1;
  323
+    return prefix ? prefix + id : id;
  324
+  },
  325
+  
  326
+  // Perform a deep comparison to check if two objects are equal.
  327
+  isEqual : function(a, b) {
  328
+    // Check object identity.
  329
+    if (a === b) return true;
  330
+    // Different types?
  331
+    var at = typeof(a), bt = typeof(b);
  332
+    if (at != bt) return false;
  333
+    // Basic equality test (watch out for coercions).
  334
+    if (a == b) return true;
  335
+    // One of them implements an isEqual()?
  336
+    if (a.isEqual) return a.isEqual(b);
  337
+    // Nothing else worked, deep compare the contents.
  338
+    return at === 'object' && _.isEqualContents(a, b);
  339
+  },
  340
+  
  341
+  // Objects have equal contents if they have the same keys, and all the values
  342
+  // are equal (as defined by _.isEqual).
  343
+  _isEqualContents : function(a, b) {
  344
+    var aKeys = _.keys(a), bKeys = _.keys(b);
  345
+    if (aKeys.length != bKeys.length) return false;
  346
+    for (var key in a) if (!_.isEqual(a[key], b[key])) return false;
  347
+    return true; 
  348
+  },
  349
+  
  350
+  // Javascript templating a-la ERB, pilfered from John Resig's 
  351
+  // "Secrets of the Javascript Ninja", page 83.
  352
+  template : function(str, data) {
  353
+    var fn = new Function('obj', 
  354
+      'var p=[],print=function(){p.push.apply(p,arguments);};' +
  355
+      'with(obj){p.push(\'' +
  356
+      str
  357
+        .replace(/[\r\t\n]/g, " ") 
  358
+        .split("<%").join("\t") 
  359
+        .replace(/((^|%>)[^\t]*)'/g, "$1\r") 
  360
+        .replace(/\t=(.*?)%>/g, "',$1,'") 
  361
+        .split("\t").join("');") 
  362
+        .split("%>").join("p.push('") 
  363
+        .split("\r").join("\\'") 
  364
+    + "');}return p.join('');");
  365
+    return data ? fn(data) : fn;  
  366
+  }
  367
+  
  368
+};

0 notes on commit 02ede85

Please sign in to comment.
Something went wrong with that request. Please try again.