@qooxdoo/framework
Version:
The JS Framework for Coders
472 lines (419 loc) • 16.9 kB
JavaScript
/* ************************************************************************
qooxdoo - the new era of web development
http://qooxdoo.org
Copyright:
2004-2011 1&1 Internet AG, Germany, http://www.1und1.de
License:
MIT: https://opensource.org/licenses/MIT
See the LICENSE file in the project's top-level directory for details.
Authors:
* Martin Wittemann (wittemann)
************************************************************************ */
/**
* This class is responsible for the normalization of the native 'Array' object.
* It checks if these methods are available and, if not, appends them to
* ensure compatibility in all browsers.
* For usage samples, check out the attached links.
*
* MDN documentation © Mozilla Contributors.
*
* @group (Polyfill)
* @use(qx.bom.client.EcmaScript)
*/
qx.Bootstrap.define("qx.lang.normalize.Array", {
statics : {
/**
* The <code>indexOf()</code> method returns the first index at which a given
* element can be found in the array, or -1 if it is not present.
*
* <a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/indexOf">MDN documentation</a> |
* <a href="http://es5.github.com/#x15.4.4.14">Annotated ES5 Spec</a>
*
* @param searchElement {var} Element to locate in the array.
* @param fromIndex {Integer?} The index to start the search at.
* If the index is greater than or equal to the array's length,
* -1 is returned, which means the array will not be searched.
* If the provided index value is a negative number, it is taken
* as the offset from the end of the array. Note: if the provided
* index is negative, the array is still searched from front to
* back. If the calculated index is less than 0, then the whole
* array will be searched. Default: 0 (Entire array is searched)
* @return {Integer} The first index at which the element was found or -1
* if the element was not found in the array
*/
indexOf : function(searchElement, fromIndex) {
if (fromIndex == null) {
fromIndex = 0;
} else if (fromIndex < 0) {
fromIndex = Math.max(0, this.length + fromIndex);
}
for (var i=fromIndex; i<this.length; i++) {
if (this[i] === searchElement) {
return i;
}
}
return -1;
},
/**
* The <code>lastIndexOf()</code> method returns the last index
* at which a given element can be found in the array, or -1 if
* it is not present. The array is searched backwards, starting at
* <code>fromIndex</code>.
*
* <a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/lastIndexOf">MDN documentation</a> |
* <a href="http://es5.github.com/#x15.4.4.15">Annotated ES5 Spec</a>
*
* @param searchElement {var} Element to locate in the array.
* @param fromIndex {Integer?} The index at which to start
* searching backwards. Defaults to the array's length, i.e. the
* whole array will be searched. If the index is greater than or
* equal to the length of the array, the whole array will be
* searched. If negative, it is taken as the offset from the end
* of the array. Note that even when the index is negative, the
* array is still searched from back to front. If the calculated
* index is less than 0, -1 is returned, i.e. the array will not
* be searched.
* @return {Integer} The last index at which the element was found or -1
* if the element was not found in the array
*/
lastIndexOf : function(searchElement, fromIndex)
{
if (fromIndex == null) {
fromIndex = this.length - 1;
} else if (fromIndex < 0) {
fromIndex = Math.max(0, this.length + fromIndex);
}
for (var i=fromIndex; i>=0; i--) {
if (this[i] === searchElement) {
return i;
}
}
return -1;
},
/**
* The <code>forEach()</code> method executes a provided function
* once per array element. You can not break the loop with this function.
* If you want to do so, use {@link #some} or {@link #every}.
*
* <a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/forEach">MDN documentation</a> |
* <a href="http://es5.github.com/#x15.4.4.18">Annotated ES5 Spec</a>
*
* @param callback {Function} Function to execute for each element.
* @param obj {Object?} Value to use as <code>this</code> when executing <code>callback</code>.
*/
forEach : function(callback, obj)
{
var l = this.length;
for (var i=0; i<l; i++)
{
var value = this[i];
if (value !== undefined) {
callback.call(obj || window, value, i, this);
}
}
},
/**
* The <code>filter()</code> method creates a new array with
* all elements that pass the test implemented by the provided
* function.
*
* <a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/filter">MDN documentation</a> |
* <a href="http://es5.github.com/#x15.4.4.20">Annotated ES5 Spec</a>
*
* @param callback {Function} Function to test each element of the array.
* @param obj {Object?} Value to use as <code>this</code> when executing <code>callback</code>.
* @return {Array} filtered array
*/
filter : function(callback, obj)
{
var res = [];
var l = this.length;
for (var i=0; i<l; i++)
{
var value = this[i];
if (value !== undefined)
{
if (callback.call(obj || window, value, i, this)) {
res.push(this[i]);
}
}
}
return res;
},
/**
* The <code>map()</code> method creates a new array with
* the results of calling a provided function on every
* element in this array.
*
* <a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/map">MDN documentation</a> |
* <a href="http://es5.github.com/#x15.4.4.19">Annotated ES5 Spec</a>
*
* @param callback {Function} Function that produces an element of the new Array,
* taking three arguments:
* <ul>
* <li><code>currentValue</code> The current element being processed in the array.</li>
* <li><code>index</code> The index of the current element being processed in the array.</li>
* <li><code>array</code> The array map was called upon.</li>
* </ul>
* @param obj {Object?} Value to use as <code>this</code> when executing <code>callback</code>.
* @return {Array} result array
*/
map : function(callback, obj)
{
var res = [];
var l = this.length;
for (var i=0; i<l; i++)
{
var value = this[i];
if (value !== undefined) {
res[i] = callback.call(obj || window, value, i, this);
}
}
return res;
},
/**
* The <code>some()</code> method tests whether some
* element in the array passes the test implemented by
* the provided function.
*
* <a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/some">MDN documentation</a> |
* <a href="http://es5.github.com/#x15.4.4.17">Annotated ES5 Spec</a>
*
* @param callback {Function} Function to test for each element.
* @param obj {Object?} Value to use as <code>this</code> when executing <code>callback</code>.
* @return {Array} result array
*/
some : function(callback, obj)
{
var l = this.length;
for (var i=0; i<l; i++)
{
var value = this[i];
if (value !== undefined)
{
if (callback.call(obj || window, value, i, this)) {
return true;
}
}
}
return false;
},
/**
* The <code>every()</code> method tests whether all elements
* in the array pass the test implemented by the provided function.
*
* <a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/every">MDN documentation</a> |
* <a href="http://es5.github.com/#x15.4.4.16">Annotated ES5 Spec</a>
*
* @param callback {Function} Function to test for each element.
* @param obj {Object?} Value to use as <code>this</code> when executing <code>callback</code>.
* @return {Array} result array
*/
every : function(callback, obj)
{
var l = this.length;
for (var i=0; i<l; i++)
{
var value = this[i];
if (value !== undefined)
{
if (!callback.call(obj || window, value, i, this)) {
return false;
}
}
}
return true;
},
/**
* The <code>find()</code> method returns a value in the array, if an element in the
* array satisfies the provided testing function. Otherwise undefined is returned.
*
* <a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/find">MDN documentation</a> |
*
* @param callback {Function} Function to test for each element.
* @param obj {Object?} Value to use as <code>this</code> when executing <code>callback</code>.
* @return {Object} result, undefined if not found
*/
find : function(callback, obj) {
if (qx.core.Environment.get("qx.debug")) {
qx.core.Assert.assertFunction(callback);
}
var l = this.length;
for (var i = 0; i < l; i++) {
var value = this[i];
if (callback.call(obj || window, value, i, this)) {
return value;
}
}
return undefined;
},
/**
* The <code>findIndex()</code> method returns an index in the array, if an element in the
* array satisfies the provided testing function. Otherwise -1 is returned.
*
* <a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/findIndex">MDN documentation</a> |
*
* @param callback {Function} Function to test for each element.
* @param obj {Object?} Value to use as <code>this</code> when executing <code>callback</code>.
* @return {Integer} the index in the array, -1 if not found
*/
findIndex : function(callback, obj) {
if (qx.core.Environment.get("qx.debug")) {
qx.core.Assert.assertFunction(callback);
}
var l = this.length;
for (var i = 0; i < l; i++) {
var value = this[i];
if (callback.call(obj || window, value, i, this)) {
return i;
}
}
return -1;
},
/**
* The <code>reduce()</code> method applies a function against
* an accumulator and each value of the array (from left-to-right)
* has to reduce it to a single value.
*
* <a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/reduce">MDN documentation</a> |
* <a href="http://es5.github.com/#x15.4.4.21">Annotated ES5 Spec</a>
*
* @param callback {Function} Function to execute on each value in
* the array, taking four arguments:
* <ul>
* <li><code>previousValue</code> The value previously returned in
* the last invocation of the callback, or initialValue, if supplied.
* (See below.)</li>
* <li><code>currentValue</code> The current element being processed in the array.</li>
* <li><code>index</code> The index of the current element being processed in the array.</li>
* <li><code>array</code> The array <code>reduce</code> was called upon.</li>
* </ul>
* @param init {Object?} Object to use as the first argument to the first call of the callback.
* @return {var} result value
*/
reduce : function(callback, init) {
if(typeof callback !== "function") {
throw new TypeError("First argument is not callable");
}
if (init === undefined && this.length === 0) {
throw new TypeError("Length is 0 and no second argument given");
}
var ret = init === undefined ? this[0] : init;
for (var i = init === undefined ? 1 : 0; i < this.length; i++) {
if (i in this) {
ret = callback.call(undefined, ret, this[i], i, this);
}
}
return ret;
},
/**
* The reduceRight() method applies a function against an
* accumulator and each value of the array (from right-to-left)
* as to reduce it to a single value.
*
* <a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/reduceRight">MDN documentation</a> |
* <a href="http://es5.github.com/#x15.4.4.22">Annotated ES5 Spec</a>
*
* @param callback {Function} Function to execute on each value in
* the array, taking four arguments:
* <ul>
* <li><code>previousValue</code> The value previously returned in
* the last invocation of the callback, or initialValue, if supplied.
* (See below.)</li>
* <li><code>currentValue</code> The current element being processed in the array.</li>
* <li><code>index</code> The index of the current element being processed in the array.</li>
* <li><code>array</code> The array <code>reduce</code> was called upon.</li>
* </ul>
* @param init {Object?} Object to use as the first argument to the first call of the callback.
* @return {var} return value
*/
reduceRight : function(callback, init) {
if (typeof callback !== "function") {
throw new TypeError("First argument is not callable");
}
if (init === undefined && this.length === 0) {
throw new TypeError("Length is 0 and no second argument given");
}
var ret = init === undefined ? this[this.length - 1] : init;
for (var i = init === undefined ? this.length - 2 : this.length - 1; i >= 0; i--) {
if (i in this) {
ret = callback.call(undefined, ret, this[i], i, this);
}
}
return ret;
},
/**
* The includes() method determines whether an array includes a certain element, returning
* true or false as appropriate.
*
* <a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/includes">MDN documentation</a> |
*
* @param searchElement {var} Element which is checked for.
* @param fromIndex {Number} Index to start search from
* @return {bool} true if element is included
*/
includes : function(searchElement, fromIndex)
{
if (this == null) {
throw new TypeError('"this" is null or not defined');
}
// 1. Let O be ? ToObject(this value).
var o = Object(this);
// 2. Let len be ? ToLength(? Get(O, "length")).
var len = o.length >>> 0;
// 3. If len is 0, return false.
if (len === 0) {
return false;
}
// 4. Let n be ? ToInteger(fromIndex).
// (If fromIndex is undefined, this step produces the value 0.)
var n = fromIndex | 0;
// 5. If n ≥ 0, then
// a. Let k be n.
// 6. Else n < 0,
// a. Let k be len + n.
// b. If k < 0, let k be 0.
var k = Math.max(n >= 0 ? n : len - Math.abs(n), 0);
function sameValueZero(x, y) {
return x === y || (typeof x === 'number' && typeof y === 'number' && isNaN(x) && isNaN(y));
}
// 7. Repeat, while k < len
while (k < len) {
// a. Let elementK be the result of ? Get(O, ! ToString(k)).
// b. If SameValueZero(searchElement, elementK) is true, return true.
if (sameValueZero(o[k], searchElement)) {
return true;
}
// c. Increase k by 1.
k++;
}
// 8. Return false
return false;
}
},
/**
* @lint environmentNonLiteralKey()
*/
defer : function(statics) {
var install = function(key, name) {
if (!qx.core.Environment.get(key)) {
Object.defineProperty(Array.prototype, name, {
enumerable: false,
value: statics[name]
});
}
};
install("ecmascript.array.indexof", "indexOf");
install("ecmascript.array.lastindexof", "lastIndexOf");
install("ecmascript.array.foreach", "forEach");
install("ecmascript.array.filter", "filter");
install("ecmascript.array.map", "map");
install("ecmascript.array.some", "some");
install("ecmascript.array.find", "find");
install("ecmascript.array.findIndex", "findIndex");
install("ecmascript.array.every", "every");
install("ecmascript.array.reduce", "reduce");
install("ecmascript.array.reduceright", "reduceRight");
install("ecmascript.array.includes", "includes");
}
});