todomvc
Version:
> Helping you select an MV\* framework
301 lines (238 loc) • 7.61 kB
JavaScript
/*
Array -- a stand-alone module for using Javascript 1.6 array features
in lame-o browsers that don't support Javascript 1.6
(c) copyright 2011-2013 Brian Cavalier and John Hann
This module is part of the cujo.js family of libraries (http://cujojs.com/).
Licensed under the MIT License at:
http://www.opensource.org/licenses/mit-license.php
*/
/*
This module is under 1kB when compiled/gzipped and is compatible with
has() pre-processors (<400 bytes when compiled for modern browsers).
wrapper API:
This module will wrap native methods to normalize array calls to
be unified across js engines that support the array methods
natively with those that don't:
define(['poly/lib/shim/array'], function (array) {
var items = [1, 2, 3];
array.forEach(items, function (item) {
console.log(item);
};
});
forEach(array, lambda [, context]);
every(array, lambda [, context]);
some(array, lambda [, context]);
filter(array, lambda [, context]);
map(array, lambda [, context]);
indexOf(arr, item [, fromIndex]);
lastIndexOf(arr, item [, fromIndex]);
reduce(arr, reduceFunc [, initialValue]);
reduceRight(arr, reduceFunc [, initialValue]);
isArray(object)
polyfill API:
You may also use this module to augment the Array.prototype of
older js engines by loading it via the poly! plugin prefix:
define(['poly!poly/lib/shim/array'], function () {
var items = [1, 2, 3];
items.forEach(function (item) {
console.log(item);
};
});
All of the wrapper API methods are shimmed and are reasonably close to
the ES5 specification, but may vary slightly in unforeseen edge cases:
var array = [1, 2, 3];
array.forEach(lambda [, context]);
array.every(lambda [, context]);
array.some(lambda [, context]);
array.filter(lambda [, context]);
array.map(lambda [, context]);
array.indexOf(item [, fromIndex]);
array.lastIndexOf(item [, fromIndex]);
array.reduce(reduceFunc [, initialValue]);
array.reduceRight(reduceFunc [, initialValue]);
Array.isArray(object)
*/
define(['./lib/_base'], function (base) {
;
var proto = Array.prototype,
toString = {}.toString,
featureMap,
toObject,
_reduce,
_find,
undef;
featureMap = {
'array-foreach': 'forEach',
'array-every': 'every',
'array-some': 'some',
'array-map': 'map',
'array-filter': 'filter',
'array-reduce': 'reduce',
'array-reduceright': 'reduceRight',
'array-indexof': 'indexOf',
'array-lastindexof': 'lastIndexOf'
};
toObject = base.createCaster(Object, 'Array');
function toArrayLike (o) {
return (base.toString(o) == '[object String]')
? o.split('')
: toObject(o);
}
function isArray (o) {
return toString.call(o) == '[object Array]';
}
function has (feature) {
var prop = featureMap[feature];
return base.isFunction(proto[prop]);
}
function returnTruthy () {
return 1;
}
function returnValue (val) {
return val;
}
/***** iterators *****/
function _iterate (arr, lambda, continueFunc, context, start, inc) {
var alo, len, i, end;
alo = toArrayLike(arr);
len = alo.length >>> 0;
if (start === undef) start = 0;
if (!inc) inc = 1;
end = inc < 0 ? -1 : len;
if (!base.isFunction(lambda)) {
throw new TypeError(lambda + ' is not a function');
}
if (start == end) {
return false;
}
if ((start <= end) ^ (inc > 0)) {
throw new TypeError('Invalid length or starting index');
}
for (i = start; i != end; i = i + inc) {
if (i in alo) {
if (!continueFunc(lambda.call(context, alo[i], i, alo), i, alo[i])) {
return false;
}
}
}
return true;
}
if (!has('array-foreach')) {
proto.forEach = function forEach (lambda) {
// arguments[+1] is to fool google closure compiler into NOT adding a function argument!
_iterate(this, lambda, returnTruthy, arguments[+1]);
};
}
if (!has('array-every')) {
proto.every = function every (lambda) {
// arguments[+1] is to fool google closure compiler into NOT adding a function argument!
return _iterate(this, lambda, returnValue, arguments[+1]);
};
}
if (!has('array-some')) {
proto.some = function some (lambda) {
// arguments[+1] is to fool google closure compiler into NOT adding a function argument!
return _iterate(this, lambda, function (val) { return !val; }, arguments[+1]);
};
}
/***** mutators *****/
if(!has('array-map')) {
proto.map = function map (lambda) {
var arr, result;
arr = this;
result = new Array(arr.length);
// arguments[+1] is to fool google closure compiler into NOT adding a function argument!
_iterate(arr, lambda, function (val, i) { result[i] = val; return 1; }, arguments[+1]);
return result;
};
}
if (!has('array-filter')) {
proto.filter = function filter (lambda) {
var arr, result;
arr = this;
result = [];
_iterate(arr, lambda, function (val, i, orig) {
// use a copy of the original value in case
// the lambda function changed it
if (val) {
result.push(orig);
}
return 1;
}, arguments[1]);
return result;
};
}
/***** reducers *****/
if (!has('array-reduce') || !has('array-reduceright')) {
_reduce = function _reduce (reduceFunc, inc, initialValue, hasInitialValue) {
var reduced, startPos, initialValuePos;
startPos = initialValuePos = inc > 0 ? -1 : toArrayLike(this).length >>> 0;
// If no initialValue, use first item of array (we know length !== 0 here)
// and adjust i to start at second item
if (!hasInitialValue) {
_iterate(this, returnValue, function (val, i) {
reduced = val;
initialValuePos = i;
}, null, startPos + inc, inc);
if (initialValuePos == startPos) {
// no intial value and no items in array!
throw new TypeError();
}
}
else {
// If initialValue provided, use it
reduced = initialValue;
}
// Do the actual reduce
_iterate(this, function (item, i, arr) {
reduced = reduceFunc(reduced, item, i, arr);
}, returnTruthy, null, initialValuePos + inc, inc);
// we have a reduced value!
return reduced;
};
if (!has('array-reduce')) {
proto.reduce = function reduce (reduceFunc /*, initialValue */) {
return _reduce.call(this, reduceFunc, 1, arguments[+1], arguments.length > 1);
};
}
if (!has('array-reduceright')) {
proto.reduceRight = function reduceRight (reduceFunc /*, initialValue */) {
return _reduce.call(this, reduceFunc, -1, arguments[+1], arguments.length > 1);
};
}
}
/***** finders *****/
if (!has('array-indexof') || !has('array-lastindexof')) {
_find = function _find (arr, item, from, forward) {
var len = toArrayLike(arr).length >>> 0, foundAt = -1;
// convert to number, or default to start or end positions
from = isNaN(from) ? (forward ? 0 : len - 1) : Number(from);
// negative means it's an offset from the end position
if (from < 0) {
from = len + from - 1;
}
_iterate(arr, returnValue, function (val, i) {
if (val === item) {
foundAt = i;
}
return foundAt == -1;
}, null, from, forward ? 1 : -1);
return foundAt;
};
if (!has('array-indexof')) {
proto.indexOf = function indexOf (item) {
// arguments[+1] is to fool google closure compiler into NOT adding a function argument!
return _find(this, item, arguments[+1], true);
};
}
if (!has('array-lastindexof')) {
proto.lastIndexOf = function lastIndexOf (item) {
// arguments[+1] is to fool google closure compiler into NOT adding a function argument!
return _find(this, item, arguments[+1], false);
};
}
}
if (!Array.isArray) {
Array.isArray = isArray;
}
});