From 217a6a6a63103b35f2290134ff5bd3416a18b6e4 Mon Sep 17 00:00:00 2001
From: Sam Gentle select.
Determine whether all of the elements match a truth test.
Delegates to ECMAScript 5's native every if available.
Aliased as all.
_.every = _.all = function(obj, iterator, context) {
- iterator = iterator || _.identity;
+ iterator || (iterator = _.identity);
var result = true;
if (obj == null) return result;
if (nativeEvery && obj.every === nativeEvery) return obj.every(iterator, context);
@@ -121,7 +121,7 @@ Aliased as all. 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.
var any = _.some = _.any = function(obj, iterator, context) {
- iterator = iterator || _.identity;
+ iterator || (iterator = _.identity);
var result = false;
if (obj == null) return result;
if (nativeSome && obj.some === nativeSome) return obj.some(iterator, context);
@@ -173,7 +173,7 @@ Aliased as contains. Use a comparator function to figure out at what index an object should be inserted so as to maintain order. Uses binary search.
_.sortedIndex = function(array, obj, iterator) {
- iterator = iterator || _.identity;
+ iterator || (iterator = _.identity);
var low = 0, high = array.length;
while (low < high) {
var mid = (low + high) >> 1;
@@ -274,8 +274,9 @@ the native Python range() function. See
return range;
};Create a function bound to a given object (assigning this, and arguments,
optionally). Binding with arguments is also known as curry.
-Delegates to ECMAScript 5's native Function.bind if available.
_.bind = function(func, obj) {
- if (nativeBind && func.bind === nativeBind) return func.bind.apply(func, slice.call(arguments, 1));
+Delegates to ECMAScript 5's native Function.bind if available.
+We check for func.bind first, to fail fast when func is undefined. _.bind = function(func, obj) {
+ if (func.bind === nativeBind && nativeBind) return nativeBind.apply(func, slice.call(arguments, 1));
var args = slice.call(arguments, 2);
return function() {
return func.apply(obj, args.concat(slice.call(arguments)));
@@ -288,7 +289,7 @@ all callbacks defined on an object belong to it. Memoize an expensive function by storing its results.
_.memoize = function(func, hasher) {
var memo = {};
- hasher = hasher || _.identity;
+ hasher || (hasher = _.identity);
return function() {
var key = hasher.apply(this, arguments);
return hasOwnProperty.call(memo, key) ? memo[key] : (memo[key] = func.apply(this, arguments));
@@ -343,95 +344,99 @@ consuming the return value of the function that follows. Retrieve the names of an object's properties. + };
Returns a function that will only be executed after being called N times.
_.barrier = function(func, times) {
+ return function() {
+ if (--times === 0) { return func.apply(this, arguments); }
+ };
+ };Retrieve the names of an object's properties.
Delegates to ECMAScript 5's native Object.keys
_.keys = nativeKeys || function(obj) {
if (obj !== Object(obj)) throw new TypeError('Invalid object');
var keys = [];
for (var key in obj) if (hasOwnProperty.call(obj, key)) keys[keys.length] = key;
return keys;
- };Retrieve the values of an object's properties.
_.values = function(obj) {
+ };Retrieve the values of an object's properties.
_.values = function(obj) {
return _.map(obj, _.identity);
- };Return a sorted list of the function names available on the object. + };
Return a sorted list of the function names available on the object.
Aliased as methods
_.functions = _.methods = function(obj) {
return _.filter(_.keys(obj), function(key){ return _.isFunction(obj[key]); }).sort();
- };Extend a given object with all the properties in passed-in object(s).
_.extend = function(obj) {
+ };Extend a given object with all the properties in passed-in object(s).
_.extend = function(obj) {
each(slice.call(arguments, 1), function(source) {
for (var prop in source) obj[prop] = source[prop];
});
return obj;
- };Fill in a given object with default properties.
_.defaults = function(obj) {
+ };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];
});
return obj;
- };Create a (shallow-cloned) duplicate of an object.
_.clone = function(obj) {
+ };Create a (shallow-cloned) duplicate of an object.
_.clone = function(obj) {
return _.isArray(obj) ? obj.slice() : _.extend({}, obj);
- };Invokes interceptor with the obj, and then returns 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 = function(obj, interceptor) {
interceptor(obj);
return obj;
- };Perform a deep comparison to check if two objects are equal.
_.isEqual = function(a, b) {Check object identity.
if (a === b) return true;Different types?
var atype = typeof(a), btype = typeof(b);
- if (atype != btype) return false;Basic equality test (watch out for coercions).
if (a == b) return true;One is falsy and the other truthy.
if ((!a && b) || (a && !b)) return false;Unwrap any wrapped objects.
if (a._chain) a = a._wrapped;
- if (b._chain) b = b._wrapped;One of them implements an isEqual()?
if (a.isEqual) return a.isEqual(b);Check dates' integer values.
if (_.isDate(a) && _.isDate(b)) return a.getTime() === b.getTime();Both are NaN?
if (_.isNaN(a) && _.isNaN(b)) return false;Compare regular expressions.
if (_.isRegExp(a) && _.isRegExp(b))
+ };Perform a deep comparison to check if two objects are equal.
_.isEqual = function(a, b) {Check object identity.
if (a === b) return true;Different types?
var atype = typeof(a), btype = typeof(b);
+ if (atype != btype) return false;Basic equality test (watch out for coercions).
if (a == b) return true;One is falsy and the other truthy.
if ((!a && b) || (a && !b)) return false;Unwrap any wrapped objects.
if (a._chain) a = a._wrapped;
+ if (b._chain) b = b._wrapped;One of them implements an isEqual()?
if (a.isEqual) return a.isEqual(b);Check dates' integer values.
if (_.isDate(a) && _.isDate(b)) return a.getTime() === b.getTime();Both are NaN?
if (_.isNaN(a) && _.isNaN(b)) return false;Compare regular expressions.
if (_.isRegExp(a) && _.isRegExp(b))
return a.source === b.source &&
a.global === b.global &&
a.ignoreCase === b.ignoreCase &&
- a.multiline === b.multiline;If a is not an object by this point, we can't handle it.
if (atype !== 'object') return false;Check for different array lengths before comparing contents.
if (a.length && (a.length !== b.length)) return false;Nothing else worked, deep compare the contents.
var aKeys = _.keys(a), bKeys = _.keys(b);Different object sizes?
if (aKeys.length != bKeys.length) return false;Recursive comparison of contents.
for (var key in a) if (!(key in b) || !_.isEqual(a[key], b[key])) return false;
+ a.multiline === b.multiline;If a is not an object by this point, we can't handle it.
if (atype !== 'object') return false;Check for different array lengths before comparing contents.
if (a.length && (a.length !== b.length)) return false;Nothing else worked, deep compare the contents.
var aKeys = _.keys(a), bKeys = _.keys(b);Different object sizes?
if (aKeys.length != bKeys.length) return false;Recursive comparison of contents.
for (var key in a) if (!(key in b) || !_.isEqual(a[key], b[key])) return false;
return true;
- };Is a given array or object empty?
_.isEmpty = function(obj) {
+ };Is a given array or object empty?
_.isEmpty = function(obj) {
if (_.isArray(obj) || _.isString(obj)) return obj.length === 0;
for (var key in obj) if (hasOwnProperty.call(obj, key)) return false;
return true;
- };Is a given value a DOM element?
_.isElement = function(obj) {
+ };Is a given value a DOM element?
_.isElement = function(obj) {
return !!(obj && obj.nodeType == 1);
- };Is a given value an array? + };
Is a given value an array? Delegates to ECMA5's native Array.isArray
_.isArray = nativeIsArray || function(obj) {
return toString.call(obj) === '[object Array]';
- };Is a given variable an arguments object?
_.isArguments = function(obj) {
+ };Is a given variable an arguments object?
_.isArguments = function(obj) {
return !!(obj && hasOwnProperty.call(obj, 'callee'));
- };Is a given value a function?
_.isFunction = function(obj) {
+ };Is a given value a function?
_.isFunction = function(obj) {
return !!(obj && obj.constructor && obj.call && obj.apply);
- };Is a given value a string?
_.isString = function(obj) {
+ };Is a given value a string?
_.isString = function(obj) {
return !!(obj === '' || (obj && obj.charCodeAt && obj.substr));
- };Is a given value a number?
_.isNumber = function(obj) {
+ };Is a given value a number?
_.isNumber = function(obj) {
return !!(obj === 0 || (obj && obj.toExponential && obj.toFixed));
- };Is the given value NaN? NaN happens to be the only value in JavaScript
+ };
Is the given value NaN? NaN happens to be the only value in JavaScript
that does not equal itself.
_.isNaN = function(obj) {
return obj !== obj;
- };Is a given value a boolean?
_.isBoolean = function(obj) {
+ };Is a given value a boolean?
_.isBoolean = function(obj) {
return obj === true || obj === false;
- };Is a given value a date?
_.isDate = function(obj) {
+ };Is a given value a date?
_.isDate = function(obj) {
return !!(obj && obj.getTimezoneOffset && obj.setUTCFullYear);
- };Is the given value a regular expression?
_.isRegExp = function(obj) {
+ };Is the given value a regular expression?
_.isRegExp = function(obj) {
return !!(obj && obj.test && obj.exec && (obj.ignoreCase || obj.ignoreCase === false));
- };Is a given value equal to null?
_.isNull = function(obj) {
+ };Is a given value equal to null?
_.isNull = function(obj) {
return obj === null;
- };Is a given variable undefined?
_.isUndefined = function(obj) {
+ };Is a given variable undefined?
_.isUndefined = function(obj) {
return obj === void 0;
- };Run Underscore.js in noConflict mode, returning the _ variable to its
+ };
Run Underscore.js in noConflict mode, returning the _ variable to its
previous owner. Returns a reference to the Underscore object.
_.noConflict = function() {
root._ = previousUnderscore;
return this;
- };Keep the identity function around for default iterators.
_.identity = function(value) {
+ };Keep the identity function around for default iterators.
_.identity = function(value) {
return value;
- };Run a function n times.
_.times = function (n, iterator, context) {
+ };Run a function n times.
_.times = function (n, iterator, context) {
for (var i = 0; i < n; i++) iterator.call(context, i);
- };Add your own custom functions to the Underscore object, ensuring that + };
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]);
});
- };Generate a unique integer id (unique within the entire client session). + };
Generate a unique integer id (unique within the entire client session). Useful for temporary DOM ids.
var idCounter = 0;
_.uniqueId = function(prefix) {
var id = idCounter++;
return prefix ? prefix + id : id;
- };By default, Underscore uses ERB-style template delimiters, change the + };
By default, Underscore uses ERB-style template delimiters, change the following template settings to use alternative delimiters.
_.templateSettings = {
evaluate : /<%([\s\S]+?)%>/g,
interpolate : /<%=([\s\S]+?)%>/g
- };JavaScript micro-templating, similar to John Resig's implementation. + };
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;
@@ -452,31 +457,31 @@ and correctly escapes quotes within interpolated code. If Underscore is called as a function, it returns a wrapped object that + };
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.
var wrapper = function(obj) { this._wrapped = obj; };Expose wrapper.prototype as _.prototype
_.prototype = wrapper.prototype;Helper function to continue chaining intermediate results.
var result = function(obj, chain) {
+underscore functions. Wrapped objects may be chained. var wrapper = function(obj) { this._wrapped = obj; };Expose wrapper.prototype as _.prototype
_.prototype = wrapper.prototype;Helper function to continue chaining intermediate results.
var result = function(obj, chain) {
return chain ? _(obj).chain() : obj;
- };A method to easily add functions to the OOP wrapper.
var addToWrapper = function(name, func) {
+ };A method to easily add functions to the OOP wrapper.
var addToWrapper = function(name, func) {
wrapper.prototype[name] = function() {
var args = slice.call(arguments);
unshift.call(args, this._wrapped);
return 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.
each(['pop', 'push', 'reverse', 'shift', 'sort', 'splice', 'unshift'], function(name) {
+ };Add all of the Underscore functions to the wrapper object.
_.mixin(_);Add all mutator Array functions to the wrapper.
each(['pop', 'push', 'reverse', 'shift', 'sort', 'splice', 'unshift'], function(name) {
var method = ArrayProto[name];
wrapper.prototype[name] = function() {
method.apply(this._wrapped, arguments);
return result(this._wrapped, this._chain);
};
- });Add all accessor Array functions to the wrapper.
each(['concat', 'join', 'slice'], function(name) {
+ });Add all accessor Array functions to the wrapper.
each(['concat', 'join', 'slice'], function(name) {
var method = ArrayProto[name];
wrapper.prototype[name] = function() {
return result(method.apply(this._wrapped, arguments), this._chain);
};
- });Start chaining a wrapped Underscore object.
wrapper.prototype.chain = function() {
+ });Start chaining a wrapped Underscore object.
wrapper.prototype.chain = function() {
this._chain = true;
return this;
- };Extracts the result from a wrapped and chained object.
wrapper.prototype.value = function() {
+ };Extracts the result from a wrapped and chained object.
wrapper.prototype.value = function() {
return this._wrapped;
};
diff --git a/test/functions.js b/test/functions.js
index 83c69d730..77ad72b39 100644
--- a/test/functions.js
+++ b/test/functions.js
@@ -137,4 +137,22 @@ $(document).ready(function() {
equals(composed('moe'), 'hi: moe!', 'in this case, the functions are also commutative');
});
+ test("functions: barrier", function() {
+ var testBarrier = function(barrierAmount, timesCalled) {
+ var barrierCalled = 0;
+ var barrier = _.barrier(function(){ barrierCalled++; }, barrierAmount);
+
+
+ while (timesCalled--) {
+ barrier();
+ }
+
+ return barrierCalled;
+ }
+
+ equals(testBarrier(5, 5), 1, "barrier(N) should fire after being called N times");
+ equals(testBarrier(5, 4), 0, "barrier(N) should not fire unless called N times");
+ equals(testBarrier(5, 6), 1, "barrier(N) should fire only once even if called more than N times");
+ });
+
});
diff --git a/underscore-min.js b/underscore-min.js
index 3de58cdff..de09889b3 100644
--- a/underscore-min.js
+++ b/underscore-min.js
@@ -5,22 +5,22 @@
// Oliver Steele's Functional, and John Resig's Micro-Templating.
// For all details and documentation:
// http://documentcloud.github.com/underscore
-(function(){var q=this,D=q._,n={},k=Array.prototype,o=Object.prototype,i=k.slice,E=k.unshift,F=o.toString,m=o.hasOwnProperty,s=k.forEach,t=k.map,u=k.reduce,v=k.reduceRight,w=k.filter,x=k.every,y=k.some,p=k.indexOf,z=k.lastIndexOf;o=Array.isArray;var G=Object.keys,A=Function.prototype.bind,c=function(a){return new l(a)};if(typeof module!=="undefined"&&module.exports){module.exports=c;c._=c}else q._=c;c.VERSION="1.1.5";var j=c.each=c.forEach=function(a,b,d){if(a!=null)if(s&&a.forEach===s)a.forEach(b,
-d);else if(c.isNumber(a.length))for(var e=0,f=a.length;e=e.computed&&(e={value:f,computed:g})});return e.value};c.min=function(a,b,d){if(!b&&c.isArray(a))return Math.min.apply(Math,a);var e={computed:Infinity};j(a,function(f,g,h){g=b?b.call(d,f,g,h):f;gh?1:0}),"value")};c.sortedIndex=
-function(a,b,d){d=d||c.identity;for(var e=0,f=a.length;e>1;d(a[g])=0})})};c.zip=function(){for(var a=i.call(arguments),b=c.max(c.pluck(a,"length")),d=Array(b),e=0;e=0;d--)b=[a[d].apply(this,b)];return b[0]}};c.keys=G||function(a){if(a!==Object(a))throw new TypeError("Invalid object");var b=[],d;for(d in a)if(m.call(a,
-d))b[b.length]=d;return b};c.values=function(a){return c.map(a,c.identity)};c.functions=c.methods=function(a){return c.filter(c.keys(a),function(b){return c.isFunction(a[b])}).sort()};c.extend=function(a){j(i.call(arguments,1),function(b){for(var d in b)a[d]=b[d]});return a};c.defaults=function(a){j(i.call(arguments,1),function(b){for(var d in b)if(a[d]==null)a[d]=b[d]});return a};c.clone=function(a){return c.isArray(a)?a.slice():c.extend({},a)};c.tap=function(a,b){b(a);return a};c.isEqual=function(a,
-b){if(a===b)return true;var d=typeof a;if(d!=typeof b)return false;if(a==b)return true;if(!a&&b||a&&!b)return false;if(a._chain)a=a._wrapped;if(b._chain)b=b._wrapped;if(a.isEqual)return a.isEqual(b);if(c.isDate(a)&&c.isDate(b))return a.getTime()===b.getTime();if(c.isNaN(a)&&c.isNaN(b))return false;if(c.isRegExp(a)&&c.isRegExp(b))return a.source===b.source&&a.global===b.global&&a.ignoreCase===b.ignoreCase&&a.multiline===b.multiline;if(d!=="object")return false;if(a.length&&a.length!==b.length)return false;
-d=c.keys(a);var e=c.keys(b);if(d.length!=e.length)return false;for(var f in a)if(!(f in b)||!c.isEqual(a[f],b[f]))return false;return true};c.isEmpty=function(a){if(c.isArray(a)||c.isString(a))return a.length===0;for(var b in a)if(m.call(a,b))return false;return true};c.isElement=function(a){return!!(a&&a.nodeType==1)};c.isArray=o||function(a){return F.call(a)==="[object Array]"};c.isArguments=function(a){return!!(a&&m.call(a,"callee"))};c.isFunction=function(a){return!!(a&&a.constructor&&a.call&&
-a.apply)};c.isString=function(a){return!!(a===""||a&&a.charCodeAt&&a.substr)};c.isNumber=function(a){return!!(a===0||a&&a.toExponential&&a.toFixed)};c.isNaN=function(a){return a!==a};c.isBoolean=function(a){return a===true||a===false};c.isDate=function(a){return!!(a&&a.getTimezoneOffset&&a.setUTCFullYear)};c.isRegExp=function(a){return!!(a&&a.test&&a.exec&&(a.ignoreCase||a.ignoreCase===false))};c.isNull=function(a){return a===null};c.isUndefined=function(a){return a===void 0};c.noConflict=function(){q._=
-D;return this};c.identity=function(a){return a};c.times=function(a,b,d){for(var e=0;e/g,interpolate:/<%=([\s\S]+?)%>/g};c.template=function(a,b){var d=c.templateSettings;d="var __p=[],print=function(){__p.push.apply(__p,arguments);};with(obj||{}){__p.push('"+a.replace(/\\/g,"\\\\").replace(/'/g,"\\'").replace(d.interpolate,
-function(e,f){return"',"+f.replace(/\\'/g,"'")+",'"}).replace(d.evaluate||null,function(e,f){return"');"+f.replace(/\\'/g,"'").replace(/[\r\n\t]/g," ")+"__p.push('"}).replace(/\r/g,"\\r").replace(/\n/g,"\\n").replace(/\t/g,"\\t")+"');}return __p.join('');";d=new Function("obj",d);return b?d(b):d};var l=function(a){this._wrapped=a};c.prototype=l.prototype;var r=function(a,b){return b?c(a).chain():a},I=function(a,b){l.prototype[a]=function(){var d=i.call(arguments);E.call(d,this._wrapped);return r(b.apply(c,
-d),this._chain)}};c.mixin(c);j(["pop","push","reverse","shift","sort","splice","unshift"],function(a){var b=k[a];l.prototype[a]=function(){b.apply(this._wrapped,arguments);return r(this._wrapped,this._chain)}});j(["concat","join","slice"],function(a){var b=k[a];l.prototype[a]=function(){return r(b.apply(this._wrapped,arguments),this._chain)}});l.prototype.chain=function(){this._chain=true;return this};l.prototype.value=function(){return this._wrapped}})();
+(function(){var p=this,C=p._,m={},i=Array.prototype,n=Object.prototype,f=i.slice,D=i.unshift,E=n.toString,l=n.hasOwnProperty,s=i.forEach,t=i.map,u=i.reduce,v=i.reduceRight,w=i.filter,x=i.every,y=i.some,o=i.indexOf,z=i.lastIndexOf;n=Array.isArray;var F=Object.keys,q=Function.prototype.bind,b=function(a){return new j(a)};typeof module!=="undefined"&&module.exports?(module.exports=b,b._=b):p._=b;b.VERSION="1.1.5";var g=b.each=b.forEach=function(a,c,d){if(a!=null)if(s&&a.forEach===s)a.forEach(c,d);else if(b.isNumber(a.length))for(var e=
+0,k=a.length;e=e.computed&&(e={value:a,computed:b})});return e.value};
+b.min=function(a,c,d){if(!c&&b.isArray(a))return Math.min.apply(Math,a);var e={computed:Infinity};g(a,function(a,b,f){b=c?c.call(d,a,b,f):a;bd?1:0}),"value")};b.sortedIndex=function(a,c,d){d||(d=b.identity);for(var e=0,f=a.length;e>1;d(a[h])=0})})};b.zip=function(){for(var a=f.call(arguments),c=b.max(b.pluck(a,"length")),d=Array(c),
+e=0;e=0;d--)b=[a[d].apply(this,b)];return b[0]}};b.barrier=function(a,b){return function(){if(--b===0)return a.apply(this,arguments)}};b.keys=F||function(a){if(a!==Object(a))throw new TypeError("Invalid object");var b=[],d;for(d in a)l.call(a,d)&&(b[b.length]=d);return b};b.values=function(a){return b.map(a,
+b.identity)};b.functions=b.methods=function(a){return b.filter(b.keys(a),function(c){return b.isFunction(a[c])}).sort()};b.extend=function(a){g(f.call(arguments,1),function(b){for(var d in b)a[d]=b[d]});return a};b.defaults=function(a){g(f.call(arguments,1),function(b){for(var d in b)a[d]==null&&(a[d]=b[d])});return a};b.clone=function(a){return b.isArray(a)?a.slice():b.extend({},a)};b.tap=function(a,b){b(a);return a};b.isEqual=function(a,c){if(a===c)return!0;var d=typeof a;if(d!=typeof c)return!1;
+if(a==c)return!0;if(!a&&c||a&&!c)return!1;if(a._chain)a=a._wrapped;if(c._chain)c=c._wrapped;if(a.isEqual)return a.isEqual(c);if(b.isDate(a)&&b.isDate(c))return a.getTime()===c.getTime();if(b.isNaN(a)&&b.isNaN(c))return!1;if(b.isRegExp(a)&&b.isRegExp(c))return a.source===c.source&&a.global===c.global&&a.ignoreCase===c.ignoreCase&&a.multiline===c.multiline;if(d!=="object")return!1;if(a.length&&a.length!==c.length)return!1;d=b.keys(a);var e=b.keys(c);if(d.length!=e.length)return!1;for(var f in a)if(!(f in
+c)||!b.isEqual(a[f],c[f]))return!1;return!0};b.isEmpty=function(a){if(b.isArray(a)||b.isString(a))return a.length===0;for(var c in a)if(l.call(a,c))return!1;return!0};b.isElement=function(a){return!!(a&&a.nodeType==1)};b.isArray=n||function(a){return E.call(a)==="[object Array]"};b.isArguments=function(a){return!(!a||!l.call(a,"callee"))};b.isFunction=function(a){return!(!a||!a.constructor||!a.call||!a.apply)};b.isString=function(a){return!!(a===""||a&&a.charCodeAt&&a.substr)};b.isNumber=function(a){return!!(a===
+0||a&&a.toExponential&&a.toFixed)};b.isNaN=function(a){return a!==a};b.isBoolean=function(a){return a===!0||a===!1};b.isDate=function(a){return!(!a||!a.getTimezoneOffset||!a.setUTCFullYear)};b.isRegExp=function(a){return!(!a||!a.test||!a.exec||!(a.ignoreCase||a.ignoreCase===!1))};b.isNull=function(a){return a===null};b.isUndefined=function(a){return a===void 0};b.noConflict=function(){p._=C;return this};b.identity=function(a){return a};b.times=function(a,b,d){for(var e=0;e/g,interpolate:/<%=([\s\S]+?)%>/g};b.template=function(a,c){var d=b.templateSettings;d="var __p=[],print=function(){__p.push.apply(__p,arguments);};with(obj||{}){__p.push('"+a.replace(/\\/g,"\\\\").replace(/'/g,"\\'").replace(d.interpolate,function(a,b){return"',"+b.replace(/\\'/g,"'")+",'"}).replace(d.evaluate||null,function(a,b){return"');"+
+b.replace(/\\'/g,"'").replace(/[\r\n\t]/g," ")+"__p.push('"}).replace(/\r/g,"\\r").replace(/\n/g,"\\n").replace(/\t/g,"\\t")+"');}return __p.join('');";d=new Function("obj",d);return c?d(c):d};var j=function(a){this._wrapped=a};b.prototype=j.prototype;var r=function(a,c){return c?b(a).chain():a},H=function(a,c){j.prototype[a]=function(){var a=f.call(arguments);D.call(a,this._wrapped);return r(c.apply(b,a),this._chain)}};b.mixin(b);g(["pop","push","reverse","shift","sort","splice","unshift"],function(a){var b=
+i[a];j.prototype[a]=function(){b.apply(this._wrapped,arguments);return r(this._wrapped,this._chain)}});g(["concat","join","slice"],function(a){var b=i[a];j.prototype[a]=function(){return r(b.apply(this._wrapped,arguments),this._chain)}});j.prototype.chain=function(){this._chain=!0;return this};j.prototype.value=function(){return this._wrapped}})();
diff --git a/underscore.js b/underscore.js
index e142ec90c..a45ca8907 100644
--- a/underscore.js
+++ b/underscore.js
@@ -510,6 +510,14 @@
};
};
+ // Returns a function that will only be executed after being called N times.
+ _.barrier = function(func, times) {
+ return function() {
+ if (--times === 0) { return func.apply(this, arguments); }
+ };
+ };
+
+
// Object Functions
// ----------------