Last active
December 27, 2015 05:49
-
-
Save cubika/7276666 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| /** | |
| * 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)); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment