Underscore.js 1.3.2
(c) 2009-2012 Jeremy Ashkenas, DocumentCloud Inc.
Underscore is freely distributable under the MIT license.
Portions of Underscore are inspired or borrowed from Prototype,
@@ -31,7 +31,7 @@
exports._ = _;
} else {
root['_'] = _;
- } |
Current version. | |
Collection Functions | |
The cornerstone, an each implementation, aka forEach .
+ } |
Current version. | |
Collection Functions | |
The cornerstone, an each implementation, aka forEach .
Handles objects with the built-in forEach , arrays, and raw objects.
Delegates to ECMAScript 5's native forEach if available. | var each = _.each = _.forEach = function(obj, iterator, context) {
if (obj == null) return;
@@ -122,7 +122,7 @@
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 . | var any = _.some = _.any = function(obj, iterator, context) {
@@ -151,7 +151,7 @@
}; |
Convenience version of a common use case of map : fetching a property. | _.pluck = function(obj, key) {
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) {
@@ -160,7 +160,7 @@
});
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) {
@@ -171,16 +171,13 @@
}; |
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) {
+ }; |
Sort the object's values by a criterion produced by an iterator. | _.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,
@@ -188,6 +185,8 @@
};
}).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
@@ -208,17 +207,17 @@
iterator(array[mid]) < iterator(obj) ? low = mid + 1 : high = mid;
}
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);
+ }; |
Safely convert anything iterable into a real, live array. | _.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
@@ -251,19 +250,19 @@
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;
- }; |
Produce an array that contains the union: each distinct element from all of
+ return results;
+ }; |
Produce an array that contains the union: each distinct element from all of
the passed-in arrays. | _.union = function() {
return _.uniq(_.flatten(arguments, true));
- }; |
Produce an array that contains every item shared between all the
+ }; |
Produce an array that contains every item shared between all the
passed-in arrays. (Aliased as "intersect" for back-compat.) | _.intersection = _.intersect = function(array) {
var rest = slice.call(arguments, 1);
return _.filter(_.uniq(array), function(item) {
@@ -271,18 +270,18 @@
return _.indexOf(other, item) >= 0;
});
});
- }; |
Take the difference between one array and a number of other arrays.
+ }; |
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
+ }; |
Zip together multiple lists into a single array -- elements that share
an index go together. | _.zip = function() {
var args = slice.call(arguments);
var length = _.max(_.pluck(args, 'length'));
var results = new Array(length);
for (var i = 0; i < length; i++) results[i] = _.pluck(args, "" + i);
return results;
- }; |
If the browser doesn't supply us with indexOf (I'm looking at you, MSIE),
+ }; |
If the browser doesn't supply us with indexOf (I'm looking at you, MSIE),
we need this function. Return the position of the first occurrence of an
item in an array, or -1 if the item is not included in the array.
Delegates to ECMAScript 5's native indexOf if available.
@@ -297,13 +296,13 @@
if (nativeIndexOf && array.indexOf === nativeIndexOf) return array.indexOf(item);
for (i = 0, l = array.length; i < l; i++) if (i in array && array[i] === item) return i;
return -1;
- }; |
Delegates to ECMAScript 5's native lastIndexOf if available. | _.lastIndexOf = function(array, item) {
+ }; |
Delegates to ECMAScript 5's native lastIndexOf if available. | _.lastIndexOf = function(array, item) {
if (array == null) return -1;
if (nativeLastIndexOf && array.lastIndexOf === nativeLastIndexOf) return array.lastIndexOf(item);
var i = array.length;
while (i--) if (i in array && array[i] === item) return i;
return -1;
- }; |
Generate an integer Array containing an arithmetic progression. A port of
+ }; |
Generate an integer Array containing an arithmetic progression. A port of
the native Python range() function. See
the Python documentation. | _.range = function(start, stop, step) {
if (arguments.length <= 1) {
@@ -322,7 +321,7 @@
}
return range;
- }; |
Function (ahem) Functions | |
Reusable constructor function for prototype setting. | |
Create a function bound to a given object (assigning this , and arguments,
+ }; |
Function (ahem) Functions | |
Reusable constructor function for prototype setting. | |
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.
We check for func.bind first, to fail fast when func is undefined. | _.bind = function bind(func, context) {
@@ -338,29 +337,29 @@
if (Object(result) === result) return result;
return self;
};
- }; |
Bind all of an object's methods to that object. Useful for ensuring that
+ }; |
Bind all of an object's methods to that object. Useful for ensuring that
all callbacks defined on an object belong to it. | _.bindAll = function(obj) {
var funcs = slice.call(arguments, 1);
if (funcs.length == 0) funcs = _.functions(obj);
each(funcs, function(f) { obj[f] = _.bind(obj[f], obj); });
return obj;
- }; |
Memoize an expensive function by storing its results. | _.memoize = function(func, hasher) {
+ }; |
Memoize an expensive function by storing its results. | _.memoize = function(func, hasher) {
var memo = {};
hasher || (hasher = _.identity);
return function() {
var key = hasher.apply(this, arguments);
return _.has(memo, key) ? memo[key] : (memo[key] = func.apply(this, arguments));
};
- }; |
Delays a function for the given number of milliseconds, and then calls
+ }; |
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);
- }; |
Defers a function, scheduling it to run after the current call stack has
+ 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) {
return _.delay.apply(_, [func, 1].concat(slice.call(arguments, 1)));
- }; |
Returns a function, that, when invoked, will only be triggered at most once
+ }; |
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;
@@ -373,25 +372,28 @@
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
+ }; |
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);
};
- }; |
Returns a function that will be executed at most one time, no matter how
+ }; |
Returns a function that will be executed at most one time, no matter how
often you call it. Useful for lazy initialization. | _.once = function(func) {
var ran = false, memo;
return function() {
@@ -399,14 +401,14 @@
ran = true;
return memo = func.apply(this, arguments);
};
- }; |
Returns the first function passed as an argument to the second,
+ }; |
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 = function(func, wrapper) {
return function() {
var args = [func].concat(slice.call(arguments, 0));
return wrapper.apply(this, args);
};
- }; |
Returns a function that is the composition of a list of functions, each
+ }; |
Returns a function that is the composition of a list of functions, each
consuming the return value of the function that follows. | _.compose = function() {
var funcs = arguments;
return function() {
@@ -416,196 +418,231 @@
}
return args[0];
};
- }; |
Returns a function that will only be executed after being called N times. | _.after = function(times, func) {
+ }; |
Returns a function that will only be executed after being called N times. | _.after = function(times, func) {
if (times <= 0) return func();
return function() {
if (--times < 1) { return func.apply(this, arguments); }
};
- }; |
Object Functions | |
Retrieve the names of an object's properties.
+ }; |
Object Functions | |
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 (_.has(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) {
var names = [];
for (var key in obj) {
if (_.isFunction(obj[key])) names.push(key);
}
return names.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) {
+ }; |
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];
}
});
return obj;
- }; |
Create a (shallow-cloned) duplicate of an object. | _.clone = function(obj) {
+ }; |
Create a (shallow-cloned) duplicate of an object. | _.clone = function(obj) {
if (!_.isObject(obj)) return 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;
- }; |
Internal recursive comparison function. | function eq(a, b, stack) { |
Identical objects are equal. 0 === -0 , but they aren't identical.
-See the Harmony egal proposal: http://wiki.ecmascript.org/doku.php?id=harmony:egal. | if (a === b) return a !== 0 || 1 / a == 1 / b; |
A strict comparison is necessary because null == undefined . | if (a == null || b == null) return a === b; |
Unwrap any wrapped objects. | if (a._chain) a = a._wrapped;
- if (b._chain) b = b._wrapped; |
Invoke a custom isEqual method if one is provided. | if (a.isEqual && _.isFunction(a.isEqual)) return a.isEqual(b);
- if (b.isEqual && _.isFunction(b.isEqual)) return b.isEqual(a); |
Compare [[Class]] names. | var className = toString.call(a);
+ }; |
Internal recursive comparison function. | function eq(a, b, stack) { |
Identical objects are equal. 0 === -0 , but they aren't identical.
+See the Harmony egal proposal: http://wiki.ecmascript.org/doku.php?id=harmony:egal. | if (a === b) return a !== 0 || 1 / a == 1 / b; |
A strict comparison is necessary because null == undefined . | if (a == null || b == null) return a === b; |
Unwrap any wrapped objects. | if (a._chain) a = a._wrapped;
+ if (b._chain) b = b._wrapped; |
Invoke a custom isEqual method if one is provided. | if (a.isEqual && _.isFunction(a.isEqual)) return a.isEqual(b);
+ if (b.isEqual && _.isFunction(b.isEqual)) return b.isEqual(a); |
Compare [[Class]] names. | var className = toString.call(a);
if (className != toString.call(b)) return false;
- switch (className) { |
Strings, numbers, dates, and booleans are compared by value. | |
Primitives and their corresponding object wrappers are equivalent; thus, "5" is
+ switch (className) { |
Strings, numbers, dates, and booleans are compared by value. | |
Primitives and their corresponding object wrappers are equivalent; thus, "5" is
equivalent to new String("5") . | return a == String(b);
- case '[object Number]': |
NaN s are equivalent, but non-reflexive. An egal comparison is performed for
+ case '[object Number]':
|
NaN s are equivalent, but non-reflexive. An egal comparison is performed for
other numeric values. | return a != +a ? b != +b : (a == 0 ? 1 / a == 1 / b : a == +b);
case '[object Date]':
- case '[object Boolean]': |
Coerce dates and booleans to numeric primitive values. Dates are compared by their
+ case '[object Boolean]': |
Coerce dates and booleans to numeric primitive values. Dates are compared by their
millisecond representations. Note that invalid dates with millisecond representations
-of NaN are not equivalent. | |
RegExps are compared by their source patterns and flags. | case '[object RegExp]':
+of NaN are not equivalent. | |
RegExps are compared by their source patterns and flags. | case '[object RegExp]':
return a.source == b.source &&
a.global == b.global &&
a.multiline == b.multiline &&
a.ignoreCase == b.ignoreCase;
}
- if (typeof a != 'object' || typeof b != 'object') return false; |
Assume equality for cyclic structures. The algorithm for detecting cyclic
+ if (typeof a != 'object' || typeof b != 'object') return false; |
Assume equality for cyclic structures. The algorithm for detecting cyclic
structures is adapted from ES 5.1 section 15.12.3, abstract operation JO . | var length = stack.length;
- while (length--) { |
Linear search. Performance is inversely proportional to the number of
+ while (length--) { |
Linear search. Performance is inversely proportional to the number of
unique nested structures. | if (stack[length] == a) return true;
- } |
Add the first object to the stack of traversed objects. | stack.push(a);
- var size = 0, result = true; |
Recursively compare objects and arrays. | if (className == '[object Array]') { |
Compare array lengths to determine if a deep comparison is necessary. | |
Add the first object to the stack of traversed objects. | stack.push(a);
+ var size = 0, result = true; |
Recursively compare objects and arrays. | if (className == '[object Array]') { |
Compare array lengths to determine if a deep comparison is necessary. | size = a.length;
result = size == b.length;
- if (result) { |
Deep compare the contents, ignoring non-numeric properties. | |
Ensure commutative equality for sparse arrays. | if (!(result = size in a == size in b && eq(a[size], b[size], stack))) break;
+ if (result) { |
Deep compare the contents, ignoring non-numeric properties. | |
Ensure commutative equality for sparse arrays. | if (!(result = size in a == size in b && eq(a[size], b[size], stack))) break;
}
}
- } else { |
Objects with different constructors are not equivalent. | if ('constructor' in a != 'constructor' in b || a.constructor != b.constructor) return false; |
Deep compare objects. | for (var key in a) {
- if (_.has(a, key)) { |
Count the expected number of properties. | |
Deep compare each member. | if (!(result = _.has(b, key) && eq(a[key], b[key], stack))) break;
+ } else { |
Objects with different constructors are not equivalent. | if ('constructor' in a != 'constructor' in b || a.constructor != b.constructor) return false; |
Deep compare objects. | for (var key in a) {
+ if (_.has(a, key)) { |
Count the expected number of properties. | |
Deep compare each member. | if (!(result = _.has(b, key) && eq(a[key], b[key], stack))) break;
}
- } |
Ensure that both objects contain the same number of properties. | |
Ensure that both objects contain the same number of properties. | if (result) {
for (key in b) {
if (_.has(b, key) && !(size--)) break;
}
result = !size;
}
- } |
Remove the first object from the stack of traversed objects. | |
Remove the first object from the stack of traversed objects. | stack.pop();
return result;
- } |
Perform a deep comparison to check if two objects are equal. | _.isEqual = function(a, b) {
+ } |
Perform a deep comparison to check if two objects are equal. | _.isEqual = function(a, b) {
return eq(a, b, []);
- }; |
Is a given array, string, or object empty?
+ }; |
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;
- }; |
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 object? | _.isObject = function(obj) {
+ }; |
Is a given variable an object? | _.isObject = function(obj) {
return obj === Object(obj);
- }; |
Is a given variable an arguments object? | _.isArguments = function(obj) {
+ }; |
Is a given variable an arguments object? | _.isArguments = function(obj) {
return toString.call(obj) == '[object Arguments]';
};
if (!_.isArguments(arguments)) {
_.isArguments = function(obj) {
return !!(obj && _.has(obj, 'callee'));
};
- } |
Is a given value a function? | _.isFunction = function(obj) {
+ } |
Is a given value a function? | _.isFunction = function(obj) {
return toString.call(obj) == '[object Function]';
- }; |
Is a given value a string? | _.isString = function(obj) {
+ }; |
Is a given value a string? | _.isString = function(obj) {
return toString.call(obj) == '[object String]';
- }; |
Is a given value a number? | _.isNumber = function(obj) {
+ }; |
Is a given value a number? | _.isNumber = function(obj) {
return toString.call(obj) == '[object Number]';
- }; |
Is the given value NaN ? | _.isNaN = function(obj) { |
NaN is the only value for which === is not reflexive. | |
Is a given value a boolean? | _.isBoolean = function(obj) {
+ }; |
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. | |
Is a given value a boolean? | _.isBoolean = function(obj) {
return obj === true || obj === false || toString.call(obj) == '[object Boolean]';
- }; |
Is a given value a date? | _.isDate = function(obj) {
+ }; |
Is a given value a date? | _.isDate = function(obj) {
return toString.call(obj) == '[object Date]';
- }; |
Is the given value a regular expression? | _.isRegExp = function(obj) {
+ }; |
Is the given value a regular expression? | _.isRegExp = function(obj) {
return toString.call(obj) == '[object RegExp]';
- }; |
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;
- }; |
Has own property? | _.has = function(obj, key) {
+ }; |
Has own property? | _.has = function(obj, key) {
return hasOwnProperty.call(obj, key);
- }; |
Utility Functions | |
Run Underscore.js in noConflict mode, returning the _ variable to its
+ }; |
Utility Functions | |
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);
- }; |
Escape a string for HTML interpolation. | _.escape = function(string) {
+ }; |
Escape a string for HTML interpolation. | _.escape = function(string) {
return (''+string).replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>').replace(/"/g, '"').replace(/'/g, ''').replace(/\//g,'/');
- }; |
Add your own custom functions to the Underscore object, ensuring that
+ }; |
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]);
});
- }; |
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,
escape : /<%-([\s\S]+?)%>/g
- }; |
When customizing templateSettings , if you don't want to define an
+ }; |
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. | |
Within an interpolation, evaluation, or escaping, remove HTML escaping
+guaranteed not to match. | |
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, "'");
- }; |
JavaScript micro-templating, similar to John Resig's implementation.
+ 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, _);
- };
- }; |
Add a "chain" function, which will delegate to the wrapper. | _.chain = function(obj) {
+and correctly escapes quotes within interpolated code. | _.template = function(text, data, settings) {
+ settings = _.extend(_.templateSettings, settings); |
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();
- }; |
The OOP Wrapper | |
If Underscore is called as a function, it returns a wrapped object that
+ }; |
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. | 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. | |
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. | |
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() {
var wrapped = this._wrapped;
@@ -614,15 +651,15 @@
if ((name == 'shift' || name == 'splice') && length === 0) delete wrapped[0];
return result(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/index.html b/index.html
index a60c8d33e..c00b98a8d 100644
--- a/index.html
+++ b/index.html
@@ -136,7 +136,7 @@
|