vendor/assets/javascripts/underscore.js in rails-backbone-0.7.1 vs vendor/assets/javascripts/underscore.js in rails-backbone-0.7.2

- old
+ new

@@ -1,6 +1,6 @@ -// Underscore.js 1.3.1 +// Underscore.js 1.3.3 // (c) 2009-2012 Jeremy Ashkenas, DocumentCloud Inc. // Underscore is freely distributable under the MIT license. // Portions of Underscore are inspired or borrowed from Prototype, // Oliver Steele's Functional, and John Resig's Micro-Templating. // For all details and documentation: @@ -60,11 +60,11 @@ } else { root['_'] = _; } // Current version. - _.VERSION = '1.3.1'; + _.VERSION = '1.3.3'; // Collection Functions // -------------------- // The cornerstone, an `each` implementation, aka `forEach`. @@ -178,11 +178,11 @@ if (obj == null) return result; if (nativeEvery && obj.every === nativeEvery) return obj.every(iterator, context); each(obj, function(value, index, list) { if (!(result = result && iterator.call(context, value, index, list))) return breaker; }); - return result; + return !!result; }; // Determine if at least one element in the object matches a truth test. // Delegates to **ECMAScript 5**'s native `some` if available. // Aliased as `any`. @@ -222,11 +222,11 @@ return _.map(obj, function(value){ return value[key]; }); }; // Return the maximum element or (element-based computation). _.max = function(obj, iterator, context) { - if (!iterator && _.isArray(obj)) return Math.max.apply(Math, obj); + if (!iterator && _.isArray(obj) && obj[0] === +obj[0]) return Math.max.apply(Math, obj); if (!iterator && _.isEmpty(obj)) return -Infinity; var result = {computed : -Infinity}; each(obj, function(value, index, list) { var computed = iterator ? iterator.call(context, value, index, list) : value; computed >= result.computed && (result = {value : value, computed : computed}); @@ -234,11 +234,11 @@ return result.value; }; // Return the minimum element (or element-based computation). _.min = function(obj, iterator, context) { - if (!iterator && _.isArray(obj)) return Math.min.apply(Math, obj); + if (!iterator && _.isArray(obj) && obj[0] === +obj[0]) return Math.min.apply(Math, obj); if (!iterator && _.isEmpty(obj)) return Infinity; var result = {computed : Infinity}; each(obj, function(value, index, list) { var computed = iterator ? iterator.call(context, value, index, list) : value; computed < result.computed && (result = {value : value, computed : computed}); @@ -248,30 +248,29 @@ // Shuffle an array. _.shuffle = function(obj) { var shuffled = [], rand; each(obj, function(value, index, list) { - if (index == 0) { - shuffled[0] = value; - } else { - rand = Math.floor(Math.random() * (index + 1)); - shuffled[index] = shuffled[rand]; - shuffled[rand] = value; - } + rand = Math.floor(Math.random() * (index + 1)); + shuffled[index] = shuffled[rand]; + shuffled[rand] = value; }); return shuffled; }; // Sort the object's values by a criterion produced by an iterator. - _.sortBy = function(obj, iterator, context) { + _.sortBy = function(obj, val, context) { + var iterator = _.isFunction(val) ? val : function(obj) { return obj[val]; }; return _.pluck(_.map(obj, function(value, index, list) { return { value : value, criteria : iterator.call(context, value, index, list) }; }).sort(function(left, right) { var a = left.criteria, b = right.criteria; + if (a === void 0) return 1; + if (b === void 0) return -1; return a < b ? -1 : a > b ? 1 : 0; }), 'value'); }; // Groups the object's values by a criterion. Pass either a string attribute @@ -297,30 +296,30 @@ } return low; }; // Safely convert anything iterable into a real, live array. - _.toArray = function(iterable) { - if (!iterable) return []; - if (iterable.toArray) return iterable.toArray(); - if (_.isArray(iterable)) return slice.call(iterable); - if (_.isArguments(iterable)) return slice.call(iterable); - return _.values(iterable); + _.toArray = function(obj) { + if (!obj) return []; + if (_.isArray(obj)) return slice.call(obj); + if (_.isArguments(obj)) return slice.call(obj); + if (obj.toArray && _.isFunction(obj.toArray)) return obj.toArray(); + return _.values(obj); }; // Return the number of elements in an object. _.size = function(obj) { - return _.toArray(obj).length; + return _.isArray(obj) ? obj.length : _.keys(obj).length; }; // Array Functions // --------------- // Get the first element of an array. Passing **n** will return the first N - // values in the array. Aliased as `head`. The **guard** check allows it to work - // with `_.map`. - _.first = _.head = function(array, n, guard) { + // values in the array. Aliased as `head` and `take`. The **guard** check + // allows it to work with `_.map`. + _.first = _.head = _.take = function(array, n, guard) { return (n != null) && !guard ? slice.call(array, 0, n) : array[0]; }; // Returns everything but the last entry of the array. Especcialy useful on // the arguments object. Passing **n** will return all the values in @@ -370,19 +369,21 @@ // Produce a duplicate-free version of the array. If the array has already // been sorted, you have the option of using a faster algorithm. // Aliased as `unique`. _.uniq = _.unique = function(array, isSorted, iterator) { var initial = iterator ? _.map(array, iterator) : array; - var result = []; - _.reduce(initial, function(memo, el, i) { - if (0 == i || (isSorted === true ? _.last(memo) != el : !_.include(memo, el))) { - memo[memo.length] = el; - result[result.length] = array[i]; + var results = []; + // The `isSorted` flag is irrelevant if the array only contains two elements. + if (array.length < 3) isSorted = true; + _.reduce(initial, function (memo, value, index) { + if (isSorted ? _.last(memo) !== value || !memo.length : !_.include(memo, value)) { + memo.push(value); + results.push(array[index]); } return memo; }, []); - return result; + return results; }; // Produce an array that contains the union: each distinct element from all of // the passed-in arrays. _.union = function() { @@ -401,11 +402,11 @@ }; // Take the difference between one array and a number of other arrays. // Only the elements present in just the first array will remain. _.difference = function(array) { - var rest = _.flatten(slice.call(arguments, 1)); + var rest = _.flatten(slice.call(arguments, 1), true); return _.filter(array, function(value){ return !_.include(rest, value); }); }; // Zip together multiple lists into a single array -- elements that share // an index go together. @@ -512,11 +513,11 @@ // Delays a function for the given number of milliseconds, and then calls // it with the arguments supplied. _.delay = function(func, wait) { var args = slice.call(arguments, 2); - return setTimeout(function(){ return func.apply(func, args); }, wait); + return setTimeout(function(){ return func.apply(null, args); }, wait); }; // Defers a function, scheduling it to run after the current call stack has // cleared. _.defer = function(func) { @@ -524,11 +525,11 @@ }; // Returns a function, that, when invoked, will only be triggered at most once // during a given window of time. _.throttle = function(func, wait) { - var context, args, timeout, throttling, more; + var context, args, timeout, throttling, more, result; var whenDone = _.debounce(function(){ more = throttling = false; }, wait); return function() { context = this; args = arguments; var later = function() { timeout = null; @@ -537,28 +538,31 @@ }; if (!timeout) timeout = setTimeout(later, wait); if (throttling) { more = true; } else { - func.apply(context, args); + result = func.apply(context, args); } whenDone(); throttling = true; + return result; }; }; // Returns a function, that, as long as it continues to be invoked, will not // be triggered. The function will be called after it stops being called for - // N milliseconds. - _.debounce = function(func, wait) { + // N milliseconds. If `immediate` is passed, trigger the function on the + // leading edge, instead of the trailing. + _.debounce = function(func, wait, immediate) { var timeout; return function() { var context = this, args = arguments; var later = function() { timeout = null; - func.apply(context, args); + if (!immediate) func.apply(context, args); }; + if (immediate && !timeout) func.apply(context, args); clearTimeout(timeout); timeout = setTimeout(later, wait); }; }; @@ -639,10 +643,19 @@ } }); return obj; }; + // Return a copy of the object only containing the whitelisted properties. + _.pick = function(obj) { + var result = {}; + each(_.flatten(slice.call(arguments, 1)), function(key) { + if (key in obj) result[key] = obj[key]; + }); + return result; + }; + // Fill in a given object with default properties. _.defaults = function(obj) { each(slice.call(arguments, 1), function(source) { for (var prop in source) { if (obj[prop] == null) obj[prop] = source[prop]; @@ -759,10 +772,11 @@ }; // Is a given array, string, or object empty? // An "empty" object has no enumerable own-properties. _.isEmpty = function(obj) { + if (obj == null) return true; if (_.isArray(obj) || _.isString(obj)) return obj.length === 0; for (var key in obj) if (_.has(obj, key)) return false; return true; }; @@ -805,10 +819,15 @@ // Is a given value a number? _.isNumber = function(obj) { return toString.call(obj) == '[object Number]'; }; + // Is a given object a finite number? + _.isFinite = function(obj) { + return _.isNumber(obj) && isFinite(obj); + }; + // Is the given value `NaN`? _.isNaN = function(obj) { // `NaN` is the only value for which `===` is not reflexive. return obj !== obj; }; @@ -866,10 +885,18 @@ // Escape a string for HTML interpolation. _.escape = function(string) { return (''+string).replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;').replace(/"/g, '&quot;').replace(/'/g, '&#x27;').replace(/\//g,'&#x2F;'); }; + // If the value of the named property is a function then invoke it; + // otherwise, return it. + _.result = function(object, property) { + if (object == null) return null; + var value = object[property]; + return _.isFunction(value) ? value.call(object) : value; + }; + // Add your own custom functions to the Underscore object, ensuring that // they're correctly added to the OOP wrapper as well. _.mixin = function(obj) { each(_.functions(obj), function(name){ addToWrapper(name, _[name] = obj[name]); @@ -895,42 +922,75 @@ // When customizing `templateSettings`, if you don't want to define an // interpolation, evaluation or escaping regex, we need one that is // guaranteed not to match. var noMatch = /.^/; + // Certain characters need to be escaped so that they can be put into a + // string literal. + var escapes = { + '\\': '\\', + "'": "'", + 'r': '\r', + 'n': '\n', + 't': '\t', + 'u2028': '\u2028', + 'u2029': '\u2029' + }; + + for (var p in escapes) escapes[escapes[p]] = p; + var escaper = /\\|'|\r|\n|\t|\u2028|\u2029/g; + var unescaper = /\\(\\|'|r|n|t|u2028|u2029)/g; + // Within an interpolation, evaluation, or escaping, remove HTML escaping // that had been previously added. var unescape = function(code) { - return code.replace(/\\\\/g, '\\').replace(/\\'/g, "'"); + return code.replace(unescaper, function(match, escape) { + return escapes[escape]; + }); }; // JavaScript micro-templating, similar to John Resig's implementation. // Underscore templating handles arbitrary delimiters, preserves whitespace, // and correctly escapes quotes within interpolated code. - _.template = function(str, data) { - var c = _.templateSettings; - var tmpl = 'var __p=[],print=function(){__p.push.apply(__p,arguments);};' + - 'with(obj||{}){__p.push(\'' + - str.replace(/\\/g, '\\\\') - .replace(/'/g, "\\'") - .replace(c.escape || noMatch, function(match, code) { - return "',_.escape(" + unescape(code) + "),'"; - }) - .replace(c.interpolate || noMatch, function(match, code) { - return "'," + unescape(code) + ",'"; - }) - .replace(c.evaluate || noMatch, function(match, code) { - return "');" + unescape(code).replace(/[\r\n\t]/g, ' ') + ";__p.push('"; - }) - .replace(/\r/g, '\\r') - .replace(/\n/g, '\\n') - .replace(/\t/g, '\\t') - + "');}return __p.join('');"; - var func = new Function('obj', '_', tmpl); - if (data) return func(data, _); - return function(data) { - return func.call(this, data, _); + _.template = function(text, data, settings) { + settings = _.defaults(settings || {}, _.templateSettings); + + // Compile the template source, taking care to escape characters that + // cannot be included in a string literal and then unescape them in code + // blocks. + var source = "__p+='" + text + .replace(escaper, function(match) { + return '\\' + escapes[match]; + }) + .replace(settings.escape || noMatch, function(match, code) { + return "'+\n_.escape(" + unescape(code) + ")+\n'"; + }) + .replace(settings.interpolate || noMatch, function(match, code) { + return "'+\n(" + unescape(code) + ")+\n'"; + }) + .replace(settings.evaluate || noMatch, function(match, code) { + return "';\n" + unescape(code) + "\n;__p+='"; + }) + "';\n"; + + // If a variable is not specified, place data values in local scope. + if (!settings.variable) source = 'with(obj||{}){\n' + source + '}\n'; + + source = "var __p='';" + + "var print=function(){__p+=Array.prototype.join.call(arguments, '')};\n" + + source + "return __p;\n"; + + var render = new Function(settings.variable || 'obj', '_', source); + if (data) return render(data, _); + var template = function(data) { + return render.call(this, data, _); }; + + // Provide the compiled function source as a convenience for build time + // precompilation. + template.source = 'function(' + (settings.variable || 'obj') + '){\n' + + source + '}'; + + return template; }; // Add a "chain" function, which will delegate to the wrapper. _.chain = function(obj) { return _(obj).chain();