X-Git-Url: http://git.ithinksw.org/extjs.git/blobdiff_plain/3789b528d8dd8aad4558e38e22d775bcab1cbd36..6746dc89c47ed01b165cc1152533605f97eb8e8d:/docs/source/Array2.html diff --git a/docs/source/Array2.html b/docs/source/Array2.html index b6bc2309..d007c7ff 100644 --- a/docs/source/Array2.html +++ b/docs/source/Array2.html @@ -29,6 +29,34 @@ var arrayPrototype = Array.prototype, slice = arrayPrototype.slice, + supportsSplice = function () { + var array = [], + lengthBefore, + j = 20; + + if (!array.splice) { + return false; + } + + // This detects a bug in IE8 splice method: + // see http://social.msdn.microsoft.com/Forums/en-US/iewebdevelopment/thread/6e946d03-e09f-4b22-a4dd-cd5e276bf05a/ + + while (j--) { + array.push("A"); + } + + array.splice(15, 0, "F", "F", "F", "F", "F","F","F","F","F","F","F","F","F","F","F","F","F","F","F","F","F"); + + lengthBefore = array.length; //41 + array.splice(13, 0, "XXX"); // add one element + + if (lengthBefore+1 != array.length) { + return false; + } + // end IE8 bug + + return true; + }(), supportsForEach = 'forEach' in arrayPrototype, supportsMap = 'map' in arrayPrototype, supportsIndexOf = 'indexOf' in arrayPrototype, @@ -41,6 +69,7 @@ }(), supportsSliceOnNodeList = true, ExtArray; + try { // IE 6 - 8 will throw an error when using Array.prototype.slice on NodeList if (typeof document !== 'undefined') { @@ -50,50 +79,173 @@ supportsSliceOnNodeList = false; } - ExtArray = Ext.Array = { - /** - * Iterates an array or an iterable value and invoke the given callback function for each item. + function fixArrayIndex (array, index) { + return (index < 0) ? Math.max(0, array.length + index) + : Math.min(array.length, index); + } - var countries = ['Vietnam', 'Singapore', 'United States', 'Russia']; + /* + Does the same work as splice, but with a slightly more convenient signature. The splice + method has bugs in IE8, so this is the implementation we use on that platform. + + The rippling of items in the array can be tricky. Consider two use cases: + + index=2 + removeCount=2 + /=====\ + +---+---+---+---+---+---+---+---+ + | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | + +---+---+---+---+---+---+---+---+ + / \/ \/ \/ \ + / /\ /\ /\ \ + / / \/ \/ \ +--------------------------+ + / / /\ /\ +--------------------------+ \ + / / / \/ +--------------------------+ \ \ + / / / /+--------------------------+ \ \ \ + / / / / \ \ \ \ + v v v v v v v v + +---+---+---+---+---+---+ +---+---+---+---+---+---+---+---+---+ + | 0 | 1 | 4 | 5 | 6 | 7 | | 0 | 1 | a | b | c | 4 | 5 | 6 | 7 | + +---+---+---+---+---+---+ +---+---+---+---+---+---+---+---+---+ + A B \=========/ + insert=[a,b,c] + + In case A, it is obvious that copying of [4,5,6,7] must be left-to-right so + that we don't end up with [0,1,6,7,6,7]. In case B, we have the opposite; we + must go right-to-left or else we would end up with [0,1,a,b,c,4,4,4,4]. + */ + function replaceSim (array, index, removeCount, insert) { + var add = insert ? insert.length : 0, + length = array.length, + pos = fixArrayIndex(array, index); + + // we try to use Array.push when we can for efficiency... + if (pos === length) { + if (add) { + array.push.apply(array, insert); + } + } else { + var remove = Math.min(removeCount, length - pos), + tailOldPos = pos + remove, + tailNewPos = tailOldPos + add - remove, + tailCount = length - tailOldPos, + lengthAfterRemove = length - remove, + i; - Ext.Array.each(countries, function(name, index, countriesItSelf) { - console.log(name); - }); + if (tailNewPos < tailOldPos) { // case A + for (i = 0; i < tailCount; ++i) { + array[tailNewPos+i] = array[tailOldPos+i]; + } + } else if (tailNewPos > tailOldPos) { // case B + for (i = tailCount; i--; ) { + array[tailNewPos+i] = array[tailOldPos+i]; + } + } // else, add == remove (nothing to do) + + if (add && pos === lengthAfterRemove) { + array.length = lengthAfterRemove; // truncate array + array.push.apply(array, insert); + } else { + array.length = lengthAfterRemove + add; // reserves space + for (i = 0; i < add; ++i) { + array[pos+i] = insert[i]; + } + } + } - var sum = function() { - var sum = 0; + return array; + } - Ext.Array.each(arguments, function(value) { - sum += value; - }); + function replaceNative (array, index, removeCount, insert) { + if (insert && insert.length) { + if (index < array.length) { + array.splice.apply(array, [index, removeCount].concat(insert)); + } else { + array.push.apply(array, insert); + } + } else { + array.splice(index, removeCount); + } + return array; + } - return sum; - }; + function eraseSim (array, index, removeCount) { + return replaceSim(array, index, removeCount); + } - sum(1, 2, 3); // returns 6 + function eraseNative (array, index, removeCount) { + array.splice(index, removeCount); + return array; + } - * The iteration can be stopped by returning false in the function callback. + function spliceSim (array, index, removeCount) { + var pos = fixArrayIndex(array, index), + removed = array.slice(index, fixArrayIndex(array, pos+removeCount)); - Ext.Array.each(countries, function(name, index, countriesItSelf) { - if (name === 'Singapore') { - return false; // break here + if (arguments.length < 4) { + replaceSim(array, pos, removeCount); + } else { + replaceSim(array, pos, removeCount, slice.call(arguments, 3)); } - }); + return removed; + } + + function spliceNative (array) { + return array.splice.apply(array, slice.call(arguments, 1)); + } + + var erase = supportsSplice ? eraseNative : eraseSim, + replace = supportsSplice ? replaceNative : replaceSim, + splice = supportsSplice ? spliceNative : spliceSim; + + // NOTE: from here on, use erase, replace or splice (not native methods)... + + ExtArray = Ext.Array = { + /** + * Iterates an array or an iterable value and invoke the given callback function for each item. + * + * var countries = ['Vietnam', 'Singapore', 'United States', 'Russia']; + * + * Ext.Array.each(countries, function(name, index, countriesItSelf) { + * console.log(name); + * }); + * + * var sum = function() { + * var sum = 0; + * + * Ext.Array.each(arguments, function(value) { + * sum += value; + * }); + * + * return sum; + * }; + * + * sum(1, 2, 3); // returns 6 + * + * The iteration can be stopped by returning false in the function callback. + * + * Ext.Array.each(countries, function(name, index, countriesItSelf) { + * if (name === 'Singapore') { + * return false; // break here + * } + * }); + * + * {@link Ext#each Ext.each} is alias for {@link Ext.Array#each Ext.Array.each} + * * @param {Array/NodeList/Mixed} iterable The value to be iterated. If this * argument is not iterable, the callback function is called once. * @param {Function} fn The callback function. If it returns false, the iteration stops and this method returns * the current `index`. Arguments passed to this callback function are: - -- `item`: {Mixed} The item at the current `index` in the passed `array` -- `index`: {Number} The current `index` within the `array` -- `allItems`: {Array/NodeList/Mixed} The `array` passed as the first argument to `Ext.Array.each` - + * + * - `item` : Mixed - The item at the current `index` in the passed `array` + * - `index` : Number - The current `index` within the `array` + * - `allItems` : Array/NodeList/Mixed - The `array` passed as the first argument to `Ext.Array.each` + * * @param {Object} scope (Optional) The scope (`this` reference) in which the specified function is executed. * @param {Boolean} reverse (Optional) Reverse the iteration order (loop from the end to the beginning) * Defaults false * @return {Boolean} See description for the `fn` parameter. - * @markdown */ each: function(array, fn, scope, reverse) { array = ExtArray.from(array); @@ -129,12 +281,11 @@ * @param {Array} array The array to iterate * @param {Function} fn The function callback, to be invoked these arguments: * -- `item`: {Mixed} The item at the current `index` in the passed `array` -- `index`: {Number} The current `index` within the `array` -- `allItems`: {Array} The `array` itself which was passed as the first argument - + * - `item` : Mixed - The item at the current `index` in the passed `array` + * - `index` : Number - The current `index` within the `array` + * - `allItems` : Array - The `array` itself which was passed as the first argument + * * @param {Object} scope (Optional) The execution scope (`this`) in which the specified function is executed. - * @markdown */ forEach: function(array, fn, scope) { if (supportsForEach) { @@ -157,7 +308,6 @@ * @param {Mixed} item The item to look for * @param {Number} from (Optional) The index at which to begin the search * @return {Number} The index of item in the array (or -1 if it is not found) - * @markdown */ indexOf: function(array, item, from) { if (supportsIndexOf) { @@ -181,7 +331,6 @@ * @param {Array} array The array to check * @param {Mixed} item The item to look for * @return {Boolean} True if the array contains the item, false otherwise - * @markdown */ contains: function(array, item) { if (supportsIndexOf) { @@ -201,28 +350,29 @@ /** * Converts any iterable (numeric indices and a length property) into a true array. - -function test() { - var args = Ext.Array.toArray(arguments), - fromSecondToLastArgs = Ext.Array.toArray(arguments, 1); - - alert(args.join(' ')); - alert(fromSecondToLastArgs.join(' ')); -} - -test('just', 'testing', 'here'); // alerts 'just testing here'; - // alerts 'testing here'; - -Ext.Array.toArray(document.getElementsByTagName('div')); // will convert the NodeList into an array -Ext.Array.toArray('splitted'); // returns ['s', 'p', 'l', 'i', 't', 't', 'e', 'd'] -Ext.Array.toArray('splitted', 0, 3); // returns ['s', 'p', 'l', 'i'] - + * + * function test() { + * var args = Ext.Array.toArray(arguments), + * fromSecondToLastArgs = Ext.Array.toArray(arguments, 1); + * + * alert(args.join(' ')); + * alert(fromSecondToLastArgs.join(' ')); + * } + * + * test('just', 'testing', 'here'); // alerts 'just testing here'; + * // alerts 'testing here'; + * + * Ext.Array.toArray(document.getElementsByTagName('div')); // will convert the NodeList into an array + * Ext.Array.toArray('splitted'); // returns ['s', 'p', 'l', 'i', 't', 't', 'e', 'd'] + * Ext.Array.toArray('splitted', 0, 3); // returns ['s', 'p', 'l', 'i'] + * + * {@link Ext#toArray Ext.toArray} is alias for {@link Ext.Array#toArray Ext.Array.toArray} + * * @param {Mixed} iterable the iterable object to be turned into a true Array. * @param {Number} start (Optional) a zero-based index that specifies the start of extraction. Defaults to 0 * @param {Number} end (Optional) a zero-based index that specifies the end of extraction. Defaults to the last * index of the iterable value * @return {Array} array - * @markdown */ toArray: function(iterable, start, end){ if (!iterable || !iterable.length) { @@ -253,8 +403,8 @@ Ext.Array.toArray('splitted', 0, 3); // returns ['s', 'p', 'l', 'i'] /** * Plucks the value of a property from each item in the Array. Example: * - Ext.Array.pluck(Ext.query("p"), "className"); // [el1.className, el2.className, ..., elN.className] - + * Ext.Array.pluck(Ext.query("p"), "className"); // [el1.className, el2.className, ..., elN.className] + * * @param {Array|NodeList} array The Array of items to pluck the value from. * @param {String} propertyName The property name to pluck from each element. * @return {Array} The value from each item in the Array. @@ -274,6 +424,7 @@ Ext.Array.toArray('splitted', 0, 3); // returns ['s', 'p', 'l', 'i'] /** * Creates a new array with the results of calling a provided function on every element in this array. + * * @param {Array} array * @param {Function} fn Callback function for each item * @param {Object} scope Callback function scope @@ -361,7 +512,8 @@ Ext.Array.toArray('splitted', 0, 3); // returns ['s', 'p', 'l', 'i'] /** * Filter through an array and remove empty item as defined in {@link Ext#isEmpty Ext.isEmpty} * - * @see Ext.Array.filter + * See {@link Ext.Array#filter} + * * @param {Array} array * @return {Array} results */ @@ -408,6 +560,7 @@ Ext.Array.toArray('splitted', 0, 3); // returns ['s', 'p', 'l', 'i'] /** * Creates a new array with all of the elements of this array for which * the provided filtering function returns true. + * * @param {Array} array * @param {Function} fn Callback function for each item * @param {Object} scope Callback function scope @@ -443,7 +596,6 @@ Ext.Array.toArray('splitted', 0, 3); // returns ['s', 'p', 'l', 'i'] * @param {Boolean} (Optional) newReference True to clone the given array and return a new reference if necessary, * defaults to false * @return {Array} array - * @markdown */ from: function(value, newReference) { if (value === undefined || value === null) { @@ -472,7 +624,7 @@ Ext.Array.toArray('splitted', 0, 3); // returns ['s', 'p', 'l', 'i'] var index = ExtArray.indexOf(array, item); if (index !== -1) { - array.splice(index, 1); + erase(array, index, 1); } return array; @@ -483,7 +635,6 @@ Ext.Array.toArray('splitted', 0, 3); // returns ['s', 'p', 'l', 'i'] * * @param {Array} array The array * @param {Mixed} item The item to include - * @return {Array} The passed array itself */ include: function(array, item) { if (!ExtArray.contains(array, item)) { @@ -504,9 +655,13 @@ Ext.Array.toArray('splitted', 0, 3); // returns ['s', 'p', 'l', 'i'] }, /** - * Merge multiple arrays into one with unique items. Alias to {@link Ext.Array#union}. + * Merge multiple arrays into one with unique items. + * + * {@link Ext.Array#union} is alias for {@link Ext.Array#merge} * - * @param {Array} array,... + * @param {Array} array1 + * @param {Array} array2 + * @param {Array} etc * @return {Array} merged */ merge: function() { @@ -524,7 +679,9 @@ Ext.Array.toArray('splitted', 0, 3); // returns ['s', 'p', 'l', 'i'] /** * Merge multiple arrays into one with unique items that exist in all of the arrays. * - * @param {Array} array,... + * @param {Array} array1 + * @param {Array} array2 + * @param {Array} etc * @return {Array} intersect */ intersect: function() { @@ -544,8 +701,8 @@ Ext.Array.toArray('splitted', 0, 3); // returns ['s', 'p', 'l', 'i'] } } - minArray = Ext.Array.unique(minArray); - arrays.splice(x, 1); + minArray = ExtArray.unique(minArray); + erase(arrays, x, 1); // Use the smallest unique'd array as the anchor loop. If the other array(s) do contain // an item in the small array, we're likely to find it before reaching the end @@ -573,8 +730,8 @@ Ext.Array.toArray('splitted', 0, 3); // returns ['s', 'p', 'l', 'i'] /** * Perform a set difference A-B by subtracting all items in array B from array A. * - * @param {Array} array A - * @param {Array} array B + * @param {Array} arrayA + * @param {Array} arrayB * @return {Array} difference */ difference: function(arrayA, arrayB) { @@ -585,7 +742,7 @@ Ext.Array.toArray('splitted', 0, 3); // returns ['s', 'p', 'l', 'i'] for (i = 0,lnB = arrayB.length; i < lnB; i++) { for (j = 0; j < ln; j++) { if (clone[j] === arrayB[i]) { - clone.splice(j, 1); + erase(clone, j, 1); j--; ln--; } @@ -595,6 +752,24 @@ Ext.Array.toArray('splitted', 0, 3); // returns ['s', 'p', 'l', 'i'] return clone; }, + /** + * Returns a shallow copy of a part of an array. This is equivalent to the native + * call "Array.prototype.slice.call(array, begin, end)". This is often used when "array" + * is "arguments" since the arguments object does not supply a slice method but can + * be the context object to Array.prototype.slice. + * + * @param {Array} array The array (or arguments object). + * @param {Number} begin The index at which to begin. Negative values are offsets from + * the end of the array. + * @param {Number} end The index at which to end. The copied items do not include + * end. Negative values are offsets from the end of the array. If end is omitted, + * all items up to the end of the array are copied. + * @return {Array} The copied piece of the array. + */ + slice: function(array, begin, end) { + return slice.call(array, begin, end); + }, + /** * Sorts the elements of an Array. * By default, this method sorts the elements alphabetically and ascending. @@ -641,8 +816,7 @@ Ext.Array.toArray('splitted', 0, 3); // returns ['s', 'p', 'l', 'i'] /** * Recursively flattens into 1-d Array. Injects Arrays inline. - * @param {Array} array The array to flatten - * @return {Array} The new, flattened array. + * */ flatten: function(array) { var worker = []; @@ -668,9 +842,10 @@ Ext.Array.toArray('splitted', 0, 3); // returns ['s', 'p', 'l', 'i'] /** * Returns the minimum value in the Array. + * * @param {Array|NodeList} array The Array from which to select the minimum value. * @param {Function} comparisonFn (optional) a function to perform the comparision which determines minimization. - * If omitted the "<" operator will be used. Note: gt = 1; eq = 0; lt = -1 + * If omitted the "<" operator will be used. Note: gt = 1; eq = 0; lt = -1 * @return {Mixed} minValue The minimum value */ min: function(array, comparisonFn) { @@ -696,10 +871,11 @@ Ext.Array.toArray('splitted', 0, 3); // returns ['s', 'p', 'l', 'i'] }, /** - * Returns the maximum value in the Array + * Returns the maximum value in the Array. + * * @param {Array|NodeList} array The Array from which to select the maximum value. * @param {Function} comparisonFn (optional) a function to perform the comparision which determines maximization. - * If omitted the ">" operator will be used. Note: gt = 1; eq = 0; lt = -1 + * If omitted the ">" operator will be used. Note: gt = 1; eq = 0; lt = -1 * @return {Mixed} maxValue The maximum value */ max: function(array, comparisonFn) { @@ -725,7 +901,8 @@ Ext.Array.toArray('splitted', 0, 3); // returns ['s', 'p', 'l', 'i'] }, /** - * Calculates the mean of all items in the array + * Calculates the mean of all items in the array. + * * @param {Array} array The Array to calculate the mean value of. * @return {Number} The mean. */ @@ -734,7 +911,8 @@ Ext.Array.toArray('splitted', 0, 3); // returns ['s', 'p', 'l', 'i'] }, /** - * Calculates the sum of all items in the given array + * Calculates the sum of all items in the given array. + * * @param {Array} array The Array to calculate the sum value of. * @return {Number} The sum. */ @@ -749,98 +927,162 @@ Ext.Array.toArray('splitted', 0, 3); // returns ['s', 'p', 'l', 'i'] } return sum; - } + }, + //<debug> + _replaceSim: replaceSim, // for unit testing + _spliceSim: spliceSim, + //</debug> + + /** + * Removes items from an array. This is functionally equivalent to the splice method + * of Array, but works around bugs in IE8's splice method and does not copy the + * removed elements in order to return them (because very often they are ignored). + * + * @param {Array} array The Array on which to replace. + * @param {Number} index The index in the array at which to operate. + * @param {Number} removeCount The number of items to remove at index. + * @return {Array} The array passed. + * @method + */ + erase: erase, + + /** + * Inserts items in to an array. + * + * @param {Array} array The Array on which to replace. + * @param {Number} index The index in the array at which to operate. + * @param {Array} items The array of items to insert at index. + * @return {Array} The array passed. + */ + insert: function (array, index, items) { + return replace(array, index, 0, items); + }, + + /** + * Replaces items in an array. This is functionally equivalent to the splice method + * of Array, but works around bugs in IE8's splice method and is often more convenient + * to call because it accepts an array of items to insert rather than use a variadic + * argument list. + * + * @param {Array} array The Array on which to replace. + * @param {Number} index The index in the array at which to operate. + * @param {Number} removeCount The number of items to remove at index (can be 0). + * @param {Array} insert An optional array of items to insert at index. + * @return {Array} The array passed. + * @method + */ + replace: replace, + + /** + * Replaces items in an array. This is equivalent to the splice method of Array, but + * works around bugs in IE8's splice method. The signature is exactly the same as the + * splice method except that the array is the first argument. All arguments following + * removeCount are inserted in the array at index. + * + * @param {Array} array The Array on which to replace. + * @param {Number} index The index in the array at which to operate. + * @param {Number} removeCount The number of items to remove at index (can be 0). + * @return {Array} An array containing the removed items. + * @method + */ + splice: splice }; /** - * Convenient alias to {@link Ext.Array#each} + * @method * @member Ext - * @method each + * @alias Ext.Array#each */ - Ext.each = Ext.Array.each; + Ext.each = ExtArray.each; /** - * Alias to {@link Ext.Array#merge}. + * @method * @member Ext.Array - * @method union + * @alias Ext.Array#merge */ - Ext.Array.union = Ext.Array.merge; + ExtArray.union = ExtArray.merge; /** * Old alias to {@link Ext.Array#min} * @deprecated 4.0.0 Use {@link Ext.Array#min} instead + * @method * @member Ext - * @method min + * @alias Ext.Array#min */ - Ext.min = Ext.Array.min; + Ext.min = ExtArray.min; /** * Old alias to {@link Ext.Array#max} * @deprecated 4.0.0 Use {@link Ext.Array#max} instead + * @method * @member Ext - * @method max + * @alias Ext.Array#max */ - Ext.max = Ext.Array.max; + Ext.max = ExtArray.max; /** * Old alias to {@link Ext.Array#sum} * @deprecated 4.0.0 Use {@link Ext.Array#sum} instead + * @method * @member Ext - * @method sum + * @alias Ext.Array#sum */ - Ext.sum = Ext.Array.sum; + Ext.sum = ExtArray.sum; /** * Old alias to {@link Ext.Array#mean} * @deprecated 4.0.0 Use {@link Ext.Array#mean} instead + * @method * @member Ext - * @method mean + * @alias Ext.Array#mean */ - Ext.mean = Ext.Array.mean; + Ext.mean = ExtArray.mean; /** * Old alias to {@link Ext.Array#flatten} * @deprecated 4.0.0 Use {@link Ext.Array#flatten} instead + * @method * @member Ext - * @method flatten + * @alias Ext.Array#flatten */ - Ext.flatten = Ext.Array.flatten; + Ext.flatten = ExtArray.flatten; /** - * Old alias to {@link Ext.Array#clean Ext.Array.clean} - * @deprecated 4.0.0 Use {@link Ext.Array.clean} instead + * Old alias to {@link Ext.Array#clean} + * @deprecated 4.0.0 Use {@link Ext.Array#clean} instead + * @method * @member Ext - * @method clean + * @alias Ext.Array#clean */ - Ext.clean = Ext.Array.clean; + Ext.clean = ExtArray.clean; /** - * Old alias to {@link Ext.Array#unique Ext.Array.unique} - * @deprecated 4.0.0 Use {@link Ext.Array.unique} instead + * Old alias to {@link Ext.Array#unique} + * @deprecated 4.0.0 Use {@link Ext.Array#unique} instead + * @method * @member Ext - * @method unique + * @alias Ext.Array#unique */ - Ext.unique = Ext.Array.unique; + Ext.unique = ExtArray.unique; /** * Old alias to {@link Ext.Array#pluck Ext.Array.pluck} * @deprecated 4.0.0 Use {@link Ext.Array#pluck Ext.Array.pluck} instead + * @method * @member Ext - * @method pluck + * @alias Ext.Array#pluck */ - Ext.pluck = Ext.Array.pluck; + Ext.pluck = ExtArray.pluck; /** - * Convenient alias to {@link Ext.Array#toArray Ext.Array.toArray} - * @param {Iterable} the iterable object to be turned into a true Array. + * @method * @member Ext - * @method toArray - * @return {Array} array + * @alias Ext.Array#toArray */ Ext.toArray = function() { return ExtArray.toArray.apply(ExtArray, arguments); - } + }; })();