mirror of
https://github.com/fergalmoran/dss.git
synced 2025-12-30 05:28:20 +00:00
591 lines
17 KiB
JavaScript
591 lines
17 KiB
JavaScript
/**
|
|
* 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)); |