/**
* Implementation of standard Array methods (introduced in ECMAScript 5th
* edition) and shorthand generics (JavaScript 1.8.5)
*
* Copyright (c) 2013 Alex K @plusdude
* http://opensource.org/licenses/MIT
*/
(function (global, infinity, undefined) {
/*jshint bitwise:false, maxlen:95, plusplus:false, validthis:true*/
"use strict";
/**
* Local references to constructors at global scope.
* This may speed up access and slightly reduce file size of minified version.
*/
var Array = global.Array;
var Object = global.Object;
var Math = global.Math;
var Number = global.Number;
/**
* Converts argument to an integral numeric value.
* @see http://www.ecma-international.org/ecma-262/5.1/#sec-9.4
*/
function toInteger(value) {
var number;
// let number be the result of calling ToNumber on the input argument
number = Number(value);
return (
// if number is NaN, return 0
number !== number ? 0 :
// if number is 0, Infinity, or -Infinity, return number
0 === number || infinity === number || -infinity === number ? number :
// return the result of computing sign(number) * floor(abs(number))
(0 < number || -1) * Math.floor(Math.abs(number))
);
}
/**
* Returns a shallow copy of a portion of an array.
* @see http://www.ecma-international.org/ecma-262/5.1/#sec-15.4.4.10
*/
function slice(begin, end) {
/*jshint newcap:false*/
var result, elements, length, index, count;
// convert elements to object
elements = Object(this);
// convert length to unsigned 32 bit integer
length = elements.length >>> 0;
// calculate begin index, if is set
if (undefined !== begin) {
// convert to integer
begin = toInteger(begin);
// handle -begin, begin > length
index = 0 > begin ? Math.max(length + begin, 0) : Math.min(begin, length);
} else {
// default value
index = 0;
}
// calculate end index, if is set
if (undefined !== end) {
// convert to integer
end = toInteger(end);
// handle -end, end > length
length = 0 > end ? Math.max(length + end, 0) : Math.min(end, length);
}
// create result array
result = new Array(length - index);
// iterate over elements
for (count = 0; index < length; ++index, ++count) {
// current index exists
if (index in elements) {
// copy current element to result array
result[count] = elements[index];
}
}
return result;
}
/**
* Returns the first index at which a given element
* can be found in the array.
* @see http://www.ecma-international.org/ecma-262/5.1/#sec-15.4.4.14
*/
function indexOf(target, begin) {
/*jshint newcap:false*/
var elements, length, index;
// convert elements to object
elements = Object(this);
// convert length to unsigned 32 bit integer
length = elements.length >>> 0;
// calculate begin index, if is set
if (undefined !== begin) {
// convert to integer
begin = toInteger(begin);
// handle -begin, begin > length
index = 0 > begin ? Math.max(length + begin, 0) : Math.min(begin, length);
} else {
// default value
index = 0;
}
// iterate over elements
for (; index < length; ++index) {
// current index exists, target element is equal to current element
if (index in elements && target === elements[index]) {
// break loop, target element found
return index;
}
}
// target element not found
return -1;
}
/**
* Returns the last index at which a given element
* can be found in the array.
* @see http://www.ecma-international.org/ecma-262/5.1/#sec-15.4.4.15
*/
function lastIndexOf(target, begin) {
/*jshint newcap:false*/
var elements, length, index;
// convert elements to object
elements = Object(this);
// convert length to unsigned 32 bit integer
length = elements.length >>> 0;
// calculate begin index, if is set
if (undefined !== begin) {
// convert to integer
begin = toInteger(begin);
// handle -begin, begin > length - 1
index = 0 > begin ? length - Math.abs(begin) : Math.min(begin, length - 1);
} else {
// default value
index = length - 1;
}
// iterate over elements backwards
for (; -1 < index; --index) {
// current index exists, target element is equal to current element
if (index in elements && target === elements[index]) {
// break loop, target element found
return index;
}
}
// target element not found
return -1;
}
/**
* Executes a provided function once per array element.
* @see http://www.ecma-international.org/ecma-262/5.1/#sec-15.4.4.18
*/
function forEach(callback, scope) {
/*jshint newcap:false*/
var elements, length, index;
// convert elements to object
elements = Object(this);
// make sure callback is a function
requireFunction(callback);
// convert length to unsigned 32 bit integer
length = elements.length >>> 0;
// iterate over elements
for (index = 0; index < length; ++index) {
// current index exists
if (index in elements) {
// execute callback
callback.call(scope, elements[index], index, elements);
}
}
}
/**
* Tests whether all elements in the array pass the test
* implemented by the provided function.
* @see http://www.ecma-international.org/ecma-262/5.1/#sec-15.4.4.16
*/
function every(callback, scope) {
/*jshint newcap:false*/
var elements, length, index;
// convert elements to object
elements = Object(this);
// make sure callback is a function
requireFunction(callback);
// convert length to unsigned 32 bit integer
length = elements.length >>> 0;
// iterate over elements
for (index = 0; index < length; ++index) {
// current index exists
if (index in elements &&
// callback returns false
!callback.call(scope, elements[index], index, elements)) {
// break loop, test failed
return false;
}
}
// test passed, controversy began..
return true;
}
/**
* Tests whether some element in the array passes the test
* implemented by the provided function.
* @see http://www.ecma-international.org/ecma-262/5.1/#sec-15.4.4.17
*/
function some(callback, scope) {
/*jshint newcap:false*/
var elements, length, index;
// convert elements to object
elements = Object(this);
// make sure callback is a function
requireFunction(callback);
// convert length to unsigned 32 bit integer
length = elements.length >>> 0;
// iterate over elements
for (index = 0; index < length; ++index) {
// current index exists
if (index in elements &&
// callback returns true
callback.call(scope, elements[index], index, elements)) {
// break loop, test passed
return true;
}
}
// test failed
return false;
}
/**
* Creates a new array with all elements that pass the test
* implemented by the provided function.
* @see http://www.ecma-international.org/ecma-262/5.1/#sec-15.4.4.20
*/
function filter(callback, scope) {
/*jshint newcap:false*/
var result = [], elements, length, index, count;
// convert elements to object
elements = Object(this);
// make sure callback is a function
requireFunction(callback);
// convert length to unsigned 32 bit integer
length = elements.length >>> 0;
// iterate over elements
for (index = count = 0; index < length; ++index) {
// current index exists
if (index in elements &&
// callback returns true
callback.call(scope, elements[index], index, elements)) {
// copy current element to result array
result[count++] = elements[index];
}
}
return result;
}
/**
* Creates a new array with the results of calling a provided function
* on every element in this array.
* @see http://www.ecma-international.org/ecma-262/5.1/#sec-15.4.4.19
*/
function map(callback, scope) {
/*jshint newcap:false*/
var result = [], elements, length, index;
// convert elements to object
elements = Object(this);
// make sure callback is a function
requireFunction(callback);
// convert length to unsigned 32 bit integer
length = elements.length >>> 0;
// iterate over elements
for (index = 0; index < length; ++index) {
// current index exists
if (index in elements) {
// copy a return value of callback to result array
result[index] = callback.call(scope, elements[index], index, elements);
}
}
return result;
}
/**
* Apply a function against values of the array (from left-to-right)
* as to reduce it to a single value.
* @see http://www.ecma-international.org/ecma-262/5.1/#sec-15.4.4.21
*/
function reduce(callback, value) {
/*jshint newcap:false*/
var elements, isset, length, index;
// convert elements to object
elements = Object(this);
// make sure callback is a function
requireFunction(callback);
// status of the initial value
isset = undefined !== value;
// convert length to unsigned 32 bit integer
length = elements.length >>> 0;
// iterate over elements
for (index = 0; index < length; ++index) {
// current index exists
if (index in elements) {
// initial value is set
if (isset) {
// replace initial value with a return value of callback
value = callback(value, elements[index], index, elements);
} else {
// current element becomes initial value
value = elements[index];
// status of the initial value
isset = true;
}
}
}
// make sure the initial value exists after iteration
requireValue(isset);
return value;
}
/**
* Apply a function against values of the array (from right-to-left)
* as to reduce it to a single value.
* @see http://www.ecma-international.org/ecma-262/5.1/#sec-15.4.4.22
*/
function reduceRight(callback, value) {
/*jshint newcap:false*/
var elements, isset, index;
// convert elements to object
elements = Object(this);
// make sure callback is a function
requireFunction(callback);
// status of the initial value
isset = undefined !== value;
// index of the last element
index = (elements.length >>> 0) - 1;
// iterate over elements backwards
for (; -1 < index; --index) {
// current index exists
if (index in elements) {
// initial value is set
if (isset) {
// replace initial value with a return value of callback
value = callback(value, elements[index], index, elements);
} else {
// current element becomes initial value
value = elements[index];
// status of the initial value
isset = true;
}
}
}
// make sure the initial value exists after iteration
requireValue(isset);
return value;
}
/**
* Returns true if an argument is an array, false if it is not.
* @see http://www.ecma-international.org/ecma-262/5.1/#sec-15.4.3.2
*/
function isArray(value) {
return "[object Array]" === Object.prototype.toString.call(value);
}
/**
* Tests if an argument is callable and throws an error if it is not.
* @private
*/
function requireFunction(value) {
if ("[object Function]" !== Object.prototype.toString.call(value)) {
throw new Error(value + " is not a function");
}
}
/**
* Throws an error if an argument can be converted to true.
* @private
*/
function requireValue(isset) {
if (!isset) {
throw new Error("reduce of empty array with no initial value");
}
}
/**
* Tests implementation of standard Array method.
* @private
*/
function supportsStandard(key) {
var support = true;
// a method exists
if (Array.prototype[key]) {
try {
// apply dummy arguments
Array.prototype[key].call(undefined, /test/, null);
// passed? implemented wrong
support = false;
} catch (e) {
// do nothing
}
} else {
support = false;
}
return support;
}
/**
* Tests implementation of generic Array method.
* @private
*/
function supportsGeneric(key) {
var support = true;
// a method exists
if (Array[key]) {
try {
// apply dummy arguments
Array[key](undefined, /test/, null);
// passed? implemented wrong
support = false;
} catch (e) {
// do nothing
}
} else {
support = false;
}
return support;
}
/**
* Assigns method to Array constructor.
* @private
*/
function extendArray(key) {
if (!supportsGeneric(key)) {
Array[key] = createGeneric(key);
}
}
/**
* Creates generic method from an instance method.
* @private
*/
function createGeneric(key) {
/** @public */
return function (elements) {
var list;
if (undefined === elements || null === elements) {
throw new Error("Array.prototype." + key + " called on " + elements);
}
list = Array.prototype.slice.call(arguments, 1);
return Array.prototype[key].apply(elements, list);
};
}
/**
* Assign ECMAScript-5 methods to Array constructor,
* and Array prototype.
*/
var ES5 = {
"indexOf": indexOf,
"lastIndexOf": lastIndexOf,
"forEach": forEach,
"every": every,
"some": some,
"filter": filter,
"map": map,
"reduce": reduce,
"reduceRight": reduceRight
};
for (var key in ES5) {
if (ES5.hasOwnProperty(key)) {
if (!supportsStandard(key)) {
Array.prototype[key] = ES5[key];
}
extendArray(key);
}
}
Array.isArray = Array.isArray || isArray;
/**
* Assign ECMAScript-3 methods to Array constructor.
* The toString method is omitted.
*/
[
"concat",
"join",
"slice",
"pop",
"push",
"reverse",
"shift",
"sort",
"splice",
"unshift"
].forEach(extendArray);
/**
* Test the slice method on DOM NodeList.
* Support: IE < 9
*/
/*jshint browser:true*/
if (document) {
try {
Array.slice(document.childNodes);
} catch (e) {
Array.prototype.slice = slice;
}
}
}(this, 1 / 0));