Upgrade to ExtJS 4.0.2 - Released 06/09/2011
[extjs.git] / src / core / src / lang / Array.js
1 /*
2
3 This file is part of Ext JS 4
4
5 Copyright (c) 2011 Sencha Inc
6
7 Contact:  http://www.sencha.com/contact
8
9 GNU General Public License Usage
10 This file may be used under the terms of the GNU General Public License version 3.0 as published by the Free Software Foundation and appearing in the file LICENSE included in the packaging of this file.  Please review the following information to ensure the GNU General Public License version 3.0 requirements will be met: http://www.gnu.org/copyleft/gpl.html.
11
12 If you are unsure which license is appropriate for your use, please contact the sales department at http://www.sencha.com/contact.
13
14 */
15 /**
16  * @author Jacky Nguyen <jacky@sencha.com>
17  * @docauthor Jacky Nguyen <jacky@sencha.com>
18  * @class Ext.Array
19  *
20  * A set of useful static methods to deal with arrays; provide missing methods for older browsers.
21
22  * @singleton
23  * @markdown
24  */
25 (function() {
26
27     var arrayPrototype = Array.prototype,
28         slice = arrayPrototype.slice,
29         supportsSplice = function () {
30             var array = [],
31                 lengthBefore,
32                 j = 20;
33
34             if (!array.splice) {
35                 return false;
36             }
37
38             // This detects a bug in IE8 splice method:
39             // see http://social.msdn.microsoft.com/Forums/en-US/iewebdevelopment/thread/6e946d03-e09f-4b22-a4dd-cd5e276bf05a/
40
41             while (j--) {
42                 array.push("A");
43             }
44
45             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");
46
47             lengthBefore = array.length; //41
48             array.splice(13, 0, "XXX"); // add one element
49
50             if (lengthBefore+1 != array.length) {
51                 return false;
52             }
53             // end IE8 bug
54
55             return true;
56         }(),
57         supportsForEach = 'forEach' in arrayPrototype,
58         supportsMap = 'map' in arrayPrototype,
59         supportsIndexOf = 'indexOf' in arrayPrototype,
60         supportsEvery = 'every' in arrayPrototype,
61         supportsSome = 'some' in arrayPrototype,
62         supportsFilter = 'filter' in arrayPrototype,
63         supportsSort = function() {
64             var a = [1,2,3,4,5].sort(function(){ return 0; });
65             return a[0] === 1 && a[1] === 2 && a[2] === 3 && a[3] === 4 && a[4] === 5;
66         }(),
67         supportsSliceOnNodeList = true,
68         ExtArray;
69
70     try {
71         // IE 6 - 8 will throw an error when using Array.prototype.slice on NodeList
72         if (typeof document !== 'undefined') {
73             slice.call(document.getElementsByTagName('body'));
74         }
75     } catch (e) {
76         supportsSliceOnNodeList = false;
77     }
78
79     function fixArrayIndex (array, index) {
80         return (index < 0) ? Math.max(0, array.length + index)
81                            : Math.min(array.length, index);
82     }
83
84     /*
85     Does the same work as splice, but with a slightly more convenient signature. The splice
86     method has bugs in IE8, so this is the implementation we use on that platform.
87
88     The rippling of items in the array can be tricky. Consider two use cases:
89
90                   index=2
91                   removeCount=2
92                  /=====\
93         +---+---+---+---+---+---+---+---+
94         | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 |
95         +---+---+---+---+---+---+---+---+
96                          /  \/  \/  \/  \
97                         /   /\  /\  /\   \
98                        /   /  \/  \/  \   +--------------------------+
99                       /   /   /\  /\   +--------------------------+   \
100                      /   /   /  \/  +--------------------------+   \   \
101                     /   /   /   /+--------------------------+   \   \   \
102                    /   /   /   /                             \   \   \   \
103                   v   v   v   v                               v   v   v   v
104         +---+---+---+---+---+---+       +---+---+---+---+---+---+---+---+---+
105         | 0 | 1 | 4 | 5 | 6 | 7 |       | 0 | 1 | a | b | c | 4 | 5 | 6 | 7 |
106         +---+---+---+---+---+---+       +---+---+---+---+---+---+---+---+---+
107         A                               B        \=========/
108                                                  insert=[a,b,c]
109
110     In case A, it is obvious that copying of [4,5,6,7] must be left-to-right so
111     that we don't end up with [0,1,6,7,6,7]. In case B, we have the opposite; we
112     must go right-to-left or else we would end up with [0,1,a,b,c,4,4,4,4].
113     */
114     function replaceSim (array, index, removeCount, insert) {
115         var add = insert ? insert.length : 0,
116             length = array.length,
117             pos = fixArrayIndex(array, index);
118
119         // we try to use Array.push when we can for efficiency...
120         if (pos === length) {
121             if (add) {
122                 array.push.apply(array, insert);
123             }
124         } else {
125             var remove = Math.min(removeCount, length - pos),
126                 tailOldPos = pos + remove,
127                 tailNewPos = tailOldPos + add - remove,
128                 tailCount = length - tailOldPos,
129                 lengthAfterRemove = length - remove,
130                 i;
131
132             if (tailNewPos < tailOldPos) { // case A
133                 for (i = 0; i < tailCount; ++i) {
134                     array[tailNewPos+i] = array[tailOldPos+i];
135                 }
136             } else if (tailNewPos > tailOldPos) { // case B
137                 for (i = tailCount; i--; ) {
138                     array[tailNewPos+i] = array[tailOldPos+i];
139                 }
140             } // else, add == remove (nothing to do)
141
142             if (add && pos === lengthAfterRemove) {
143                 array.length = lengthAfterRemove; // truncate array
144                 array.push.apply(array, insert);
145             } else {
146                 array.length = lengthAfterRemove + add; // reserves space
147                 for (i = 0; i < add; ++i) {
148                     array[pos+i] = insert[i];
149                 }
150             }
151         }
152
153         return array;
154     }
155
156     function replaceNative (array, index, removeCount, insert) {
157         if (insert && insert.length) {
158             if (index < array.length) {
159                 array.splice.apply(array, [index, removeCount].concat(insert));
160             } else {
161                 array.push.apply(array, insert);
162             }
163         } else {
164             array.splice(index, removeCount);
165         }
166         return array;
167     }
168
169     function eraseSim (array, index, removeCount) {
170         return replaceSim(array, index, removeCount);
171     }
172
173     function eraseNative (array, index, removeCount) {
174         array.splice(index, removeCount);
175         return array;
176     }
177
178     function spliceSim (array, index, removeCount) {
179         var pos = fixArrayIndex(array, index),
180             removed = array.slice(index, fixArrayIndex(array, pos+removeCount));
181
182         if (arguments.length < 4) {
183             replaceSim(array, pos, removeCount);
184         } else {
185             replaceSim(array, pos, removeCount, slice.call(arguments, 3));
186         }
187
188         return removed;
189     }
190
191     function spliceNative (array) {
192         return array.splice.apply(array, slice.call(arguments, 1));
193     }
194
195     var erase = supportsSplice ? eraseNative : eraseSim,
196         replace = supportsSplice ? replaceNative : replaceSim,
197         splice = supportsSplice ? spliceNative : spliceSim;
198
199     // NOTE: from here on, use erase, replace or splice (not native methods)...
200
201     ExtArray = Ext.Array = {
202         /**
203          * Iterates an array or an iterable value and invoke the given callback function for each item.
204          *
205          *     var countries = ['Vietnam', 'Singapore', 'United States', 'Russia'];
206          *
207          *     Ext.Array.each(countries, function(name, index, countriesItSelf) {
208          *         console.log(name);
209          *     });
210          *
211          *     var sum = function() {
212          *         var sum = 0;
213          *
214          *         Ext.Array.each(arguments, function(value) {
215          *             sum += value;
216          *         });
217          *
218          *         return sum;
219          *     };
220          *
221          *     sum(1, 2, 3); // returns 6
222          *
223          * The iteration can be stopped by returning false in the function callback.
224          *
225          *     Ext.Array.each(countries, function(name, index, countriesItSelf) {
226          *         if (name === 'Singapore') {
227          *             return false; // break here
228          *         }
229          *     });
230          *
231          * {@link Ext#each Ext.each} is alias for {@link Ext.Array#each Ext.Array.each}
232          *
233          * @param {Array/NodeList/Mixed} iterable The value to be iterated. If this
234          * argument is not iterable, the callback function is called once.
235          * @param {Function} fn The callback function. If it returns false, the iteration stops and this method returns
236          * the current `index`. Arguments passed to this callback function are:
237          *
238          * - `item` : Mixed - The item at the current `index` in the passed `array`
239          * - `index` : Number - The current `index` within the `array`
240          * - `allItems` : Array/NodeList/Mixed - The `array` passed as the first argument to `Ext.Array.each`
241          *
242          * @param {Object} scope (Optional) The scope (`this` reference) in which the specified function is executed.
243          * @param {Boolean} reverse (Optional) Reverse the iteration order (loop from the end to the beginning)
244          * Defaults false
245          * @return {Boolean} See description for the `fn` parameter.
246          */
247         each: function(array, fn, scope, reverse) {
248             array = ExtArray.from(array);
249
250             var i,
251                 ln = array.length;
252
253             if (reverse !== true) {
254                 for (i = 0; i < ln; i++) {
255                     if (fn.call(scope || array[i], array[i], i, array) === false) {
256                         return i;
257                     }
258                 }
259             }
260             else {
261                 for (i = ln - 1; i > -1; i--) {
262                     if (fn.call(scope || array[i], array[i], i, array) === false) {
263                         return i;
264                     }
265                 }
266             }
267
268             return true;
269         },
270
271         /**
272          * Iterates an array and invoke the given callback function for each item. Note that this will simply
273          * delegate to the native Array.prototype.forEach method if supported.
274          * It doesn't support stopping the iteration by returning false in the callback function like
275          * {@link Ext.Array#each}. However, performance could be much better in modern browsers comparing with
276          * {@link Ext.Array#each}
277          *
278          * @param {Array} array The array to iterate
279          * @param {Function} fn The function callback, to be invoked these arguments:
280          *
281          * - `item` : Mixed - The item at the current `index` in the passed `array`
282          * - `index` : Number - The current `index` within the `array`
283          * - `allItems` : Array - The `array` itself which was passed as the first argument
284          *
285          * @param {Object} scope (Optional) The execution scope (`this`) in which the specified function is executed.
286          */
287         forEach: function(array, fn, scope) {
288             if (supportsForEach) {
289                 return array.forEach(fn, scope);
290             }
291
292             var i = 0,
293                 ln = array.length;
294
295             for (; i < ln; i++) {
296                 fn.call(scope, array[i], i, array);
297             }
298         },
299
300         /**
301          * Get the index of the provided `item` in the given `array`, a supplement for the
302          * missing arrayPrototype.indexOf in Internet Explorer.
303          *
304          * @param {Array} array The array to check
305          * @param {Mixed} item The item to look for
306          * @param {Number} from (Optional) The index at which to begin the search
307          * @return {Number} The index of item in the array (or -1 if it is not found)
308          */
309         indexOf: function(array, item, from) {
310             if (supportsIndexOf) {
311                 return array.indexOf(item, from);
312             }
313
314             var i, length = array.length;
315
316             for (i = (from < 0) ? Math.max(0, length + from) : from || 0; i < length; i++) {
317                 if (array[i] === item) {
318                     return i;
319                 }
320             }
321
322             return -1;
323         },
324
325         /**
326          * Checks whether or not the given `array` contains the specified `item`
327          *
328          * @param {Array} array The array to check
329          * @param {Mixed} item The item to look for
330          * @return {Boolean} True if the array contains the item, false otherwise
331          */
332         contains: function(array, item) {
333             if (supportsIndexOf) {
334                 return array.indexOf(item) !== -1;
335             }
336
337             var i, ln;
338
339             for (i = 0, ln = array.length; i < ln; i++) {
340                 if (array[i] === item) {
341                     return true;
342                 }
343             }
344
345             return false;
346         },
347
348         /**
349          * Converts any iterable (numeric indices and a length property) into a true array.
350          *
351          *     function test() {
352          *         var args = Ext.Array.toArray(arguments),
353          *             fromSecondToLastArgs = Ext.Array.toArray(arguments, 1);
354          *
355          *         alert(args.join(' '));
356          *         alert(fromSecondToLastArgs.join(' '));
357          *     }
358          *
359          *     test('just', 'testing', 'here'); // alerts 'just testing here';
360          *                                      // alerts 'testing here';
361          *
362          *     Ext.Array.toArray(document.getElementsByTagName('div')); // will convert the NodeList into an array
363          *     Ext.Array.toArray('splitted'); // returns ['s', 'p', 'l', 'i', 't', 't', 'e', 'd']
364          *     Ext.Array.toArray('splitted', 0, 3); // returns ['s', 'p', 'l', 'i']
365          *
366          * {@link Ext#toArray Ext.toArray} is alias for {@link Ext.Array#toArray Ext.Array.toArray}
367          *
368          * @param {Mixed} iterable the iterable object to be turned into a true Array.
369          * @param {Number} start (Optional) a zero-based index that specifies the start of extraction. Defaults to 0
370          * @param {Number} end (Optional) a zero-based index that specifies the end of extraction. Defaults to the last
371          * index of the iterable value
372          * @return {Array} array
373          */
374         toArray: function(iterable, start, end){
375             if (!iterable || !iterable.length) {
376                 return [];
377             }
378
379             if (typeof iterable === 'string') {
380                 iterable = iterable.split('');
381             }
382
383             if (supportsSliceOnNodeList) {
384                 return slice.call(iterable, start || 0, end || iterable.length);
385             }
386
387             var array = [],
388                 i;
389
390             start = start || 0;
391             end = end ? ((end < 0) ? iterable.length + end : end) : iterable.length;
392
393             for (i = start; i < end; i++) {
394                 array.push(iterable[i]);
395             }
396
397             return array;
398         },
399
400         /**
401          * Plucks the value of a property from each item in the Array. Example:
402          *
403          *     Ext.Array.pluck(Ext.query("p"), "className"); // [el1.className, el2.className, ..., elN.className]
404          *
405          * @param {Array|NodeList} array The Array of items to pluck the value from.
406          * @param {String} propertyName The property name to pluck from each element.
407          * @return {Array} The value from each item in the Array.
408          */
409         pluck: function(array, propertyName) {
410             var ret = [],
411                 i, ln, item;
412
413             for (i = 0, ln = array.length; i < ln; i++) {
414                 item = array[i];
415
416                 ret.push(item[propertyName]);
417             }
418
419             return ret;
420         },
421
422         /**
423          * Creates a new array with the results of calling a provided function on every element in this array.
424          *
425          * @param {Array} array
426          * @param {Function} fn Callback function for each item
427          * @param {Object} scope Callback function scope
428          * @return {Array} results
429          */
430         map: function(array, fn, scope) {
431             if (supportsMap) {
432                 return array.map(fn, scope);
433             }
434
435             var results = [],
436                 i = 0,
437                 len = array.length;
438
439             for (; i < len; i++) {
440                 results[i] = fn.call(scope, array[i], i, array);
441             }
442
443             return results;
444         },
445
446         /**
447          * Executes the specified function for each array element until the function returns a falsy value.
448          * If such an item is found, the function will return false immediately.
449          * Otherwise, it will return true.
450          *
451          * @param {Array} array
452          * @param {Function} fn Callback function for each item
453          * @param {Object} scope Callback function scope
454          * @return {Boolean} True if no false value is returned by the callback function.
455          */
456         every: function(array, fn, scope) {
457             //<debug>
458             if (!fn) {
459                 Ext.Error.raise('Ext.Array.every must have a callback function passed as second argument.');
460             }
461             //</debug>
462             if (supportsEvery) {
463                 return array.every(fn, scope);
464             }
465
466             var i = 0,
467                 ln = array.length;
468
469             for (; i < ln; ++i) {
470                 if (!fn.call(scope, array[i], i, array)) {
471                     return false;
472                 }
473             }
474
475             return true;
476         },
477
478         /**
479          * Executes the specified function for each array element until the function returns a truthy value.
480          * If such an item is found, the function will return true immediately. Otherwise, it will return false.
481          *
482          * @param {Array} array
483          * @param {Function} fn Callback function for each item
484          * @param {Object} scope Callback function scope
485          * @return {Boolean} True if the callback function returns a truthy value.
486          */
487         some: function(array, fn, scope) {
488             //<debug>
489             if (!fn) {
490                 Ext.Error.raise('Ext.Array.some must have a callback function passed as second argument.');
491             }
492             //</debug>
493             if (supportsSome) {
494                 return array.some(fn, scope);
495             }
496
497             var i = 0,
498                 ln = array.length;
499
500             for (; i < ln; ++i) {
501                 if (fn.call(scope, array[i], i, array)) {
502                     return true;
503                 }
504             }
505
506             return false;
507         },
508
509         /**
510          * Filter through an array and remove empty item as defined in {@link Ext#isEmpty Ext.isEmpty}
511          *
512          * See {@link Ext.Array#filter}
513          *
514          * @param {Array} array
515          * @return {Array} results
516          */
517         clean: function(array) {
518             var results = [],
519                 i = 0,
520                 ln = array.length,
521                 item;
522
523             for (; i < ln; i++) {
524                 item = array[i];
525
526                 if (!Ext.isEmpty(item)) {
527                     results.push(item);
528                 }
529             }
530
531             return results;
532         },
533
534         /**
535          * Returns a new array with unique items
536          *
537          * @param {Array} array
538          * @return {Array} results
539          */
540         unique: function(array) {
541             var clone = [],
542                 i = 0,
543                 ln = array.length,
544                 item;
545
546             for (; i < ln; i++) {
547                 item = array[i];
548
549                 if (ExtArray.indexOf(clone, item) === -1) {
550                     clone.push(item);
551                 }
552             }
553
554             return clone;
555         },
556
557         /**
558          * Creates a new array with all of the elements of this array for which
559          * the provided filtering function returns true.
560          *
561          * @param {Array} array
562          * @param {Function} fn Callback function for each item
563          * @param {Object} scope Callback function scope
564          * @return {Array} results
565          */
566         filter: function(array, fn, scope) {
567             if (supportsFilter) {
568                 return array.filter(fn, scope);
569             }
570
571             var results = [],
572                 i = 0,
573                 ln = array.length;
574
575             for (; i < ln; i++) {
576                 if (fn.call(scope, array[i], i, array)) {
577                     results.push(array[i]);
578                 }
579             }
580
581             return results;
582         },
583
584         /**
585          * Converts a value to an array if it's not already an array; returns:
586          *
587          * - An empty array if given value is `undefined` or `null`
588          * - Itself if given value is already an array
589          * - An array copy if given value is {@link Ext#isIterable iterable} (arguments, NodeList and alike)
590          * - An array with one item which is the given value, otherwise
591          *
592          * @param {Array/Mixed} value The value to convert to an array if it's not already is an array
593          * @param {Boolean} (Optional) newReference True to clone the given array and return a new reference if necessary,
594          * defaults to false
595          * @return {Array} array
596          */
597         from: function(value, newReference) {
598             if (value === undefined || value === null) {
599                 return [];
600             }
601
602             if (Ext.isArray(value)) {
603                 return (newReference) ? slice.call(value) : value;
604             }
605
606             if (value && value.length !== undefined && typeof value !== 'string') {
607                 return Ext.toArray(value);
608             }
609
610             return [value];
611         },
612
613         /**
614          * Removes the specified item from the array if it exists
615          *
616          * @param {Array} array The array
617          * @param {Mixed} item The item to remove
618          * @return {Array} The passed array itself
619          */
620         remove: function(array, item) {
621             var index = ExtArray.indexOf(array, item);
622
623             if (index !== -1) {
624                 erase(array, index, 1);
625             }
626
627             return array;
628         },
629
630         /**
631          * Push an item into the array only if the array doesn't contain it yet
632          *
633          * @param {Array} array The array
634          * @param {Mixed} item The item to include
635          */
636         include: function(array, item) {
637             if (!ExtArray.contains(array, item)) {
638                 array.push(item);
639             }
640         },
641
642         /**
643          * Clone a flat array without referencing the previous one. Note that this is different
644          * from Ext.clone since it doesn't handle recursive cloning. It's simply a convenient, easy-to-remember method
645          * for Array.prototype.slice.call(array)
646          *
647          * @param {Array} array The array
648          * @return {Array} The clone array
649          */
650         clone: function(array) {
651             return slice.call(array);
652         },
653
654         /**
655          * Merge multiple arrays into one with unique items.
656          *
657          * {@link Ext.Array#union} is alias for {@link Ext.Array#merge}
658          *
659          * @param {Array} array1
660          * @param {Array} array2
661          * @param {Array} etc
662          * @return {Array} merged
663          */
664         merge: function() {
665             var args = slice.call(arguments),
666                 array = [],
667                 i, ln;
668
669             for (i = 0, ln = args.length; i < ln; i++) {
670                 array = array.concat(args[i]);
671             }
672
673             return ExtArray.unique(array);
674         },
675
676         /**
677          * Merge multiple arrays into one with unique items that exist in all of the arrays.
678          *
679          * @param {Array} array1
680          * @param {Array} array2
681          * @param {Array} etc
682          * @return {Array} intersect
683          */
684         intersect: function() {
685             var intersect = [],
686                 arrays = slice.call(arguments),
687                 i, j, k, minArray, array, x, y, ln, arraysLn, arrayLn;
688
689             if (!arrays.length) {
690                 return intersect;
691             }
692
693             // Find the smallest array
694             for (i = x = 0,ln = arrays.length; i < ln,array = arrays[i]; i++) {
695                 if (!minArray || array.length < minArray.length) {
696                     minArray = array;
697                     x = i;
698                 }
699             }
700
701             minArray = ExtArray.unique(minArray);
702             erase(arrays, x, 1);
703
704             // Use the smallest unique'd array as the anchor loop. If the other array(s) do contain
705             // an item in the small array, we're likely to find it before reaching the end
706             // of the inner loop and can terminate the search early.
707             for (i = 0,ln = minArray.length; i < ln,x = minArray[i]; i++) {
708                 var count = 0;
709
710                 for (j = 0,arraysLn = arrays.length; j < arraysLn,array = arrays[j]; j++) {
711                     for (k = 0,arrayLn = array.length; k < arrayLn,y = array[k]; k++) {
712                         if (x === y) {
713                             count++;
714                             break;
715                         }
716                     }
717                 }
718
719                 if (count === arraysLn) {
720                     intersect.push(x);
721                 }
722             }
723
724             return intersect;
725         },
726
727         /**
728          * Perform a set difference A-B by subtracting all items in array B from array A.
729          *
730          * @param {Array} arrayA
731          * @param {Array} arrayB
732          * @return {Array} difference
733          */
734         difference: function(arrayA, arrayB) {
735             var clone = slice.call(arrayA),
736                 ln = clone.length,
737                 i, j, lnB;
738
739             for (i = 0,lnB = arrayB.length; i < lnB; i++) {
740                 for (j = 0; j < ln; j++) {
741                     if (clone[j] === arrayB[i]) {
742                         erase(clone, j, 1);
743                         j--;
744                         ln--;
745                     }
746                 }
747             }
748
749             return clone;
750         },
751
752         /**
753          * Returns a shallow copy of a part of an array. This is equivalent to the native
754          * call "Array.prototype.slice.call(array, begin, end)". This is often used when "array"
755          * is "arguments" since the arguments object does not supply a slice method but can
756          * be the context object to Array.prototype.slice.
757          *
758          * @param {Array} array The array (or arguments object).
759          * @param {Number} begin The index at which to begin. Negative values are offsets from
760          * the end of the array.
761          * @param {Number} end The index at which to end. The copied items do not include
762          * end. Negative values are offsets from the end of the array. If end is omitted,
763          * all items up to the end of the array are copied.
764          * @return {Array} The copied piece of the array.
765          */
766         slice: function(array, begin, end) {
767             return slice.call(array, begin, end);
768         },
769
770         /**
771          * Sorts the elements of an Array.
772          * By default, this method sorts the elements alphabetically and ascending.
773          *
774          * @param {Array} array The array to sort.
775          * @param {Function} sortFn (optional) The comparison function.
776          * @return {Array} The sorted array.
777          */
778         sort: function(array, sortFn) {
779             if (supportsSort) {
780                 if (sortFn) {
781                     return array.sort(sortFn);
782                 } else {
783                     return array.sort();
784                 }
785             }
786
787             var length = array.length,
788                 i = 0,
789                 comparison,
790                 j, min, tmp;
791
792             for (; i < length; i++) {
793                 min = i;
794                 for (j = i + 1; j < length; j++) {
795                     if (sortFn) {
796                         comparison = sortFn(array[j], array[min]);
797                         if (comparison < 0) {
798                             min = j;
799                         }
800                     } else if (array[j] < array[min]) {
801                         min = j;
802                     }
803                 }
804                 if (min !== i) {
805                     tmp = array[i];
806                     array[i] = array[min];
807                     array[min] = tmp;
808                 }
809             }
810
811             return array;
812         },
813
814         /**
815          * Recursively flattens into 1-d Array. Injects Arrays inline.
816          *
817          */
818         flatten: function(array) {
819             var worker = [];
820
821             function rFlatten(a) {
822                 var i, ln, v;
823
824                 for (i = 0, ln = a.length; i < ln; i++) {
825                     v = a[i];
826
827                     if (Ext.isArray(v)) {
828                         rFlatten(v);
829                     } else {
830                         worker.push(v);
831                     }
832                 }
833
834                 return worker;
835             }
836
837             return rFlatten(array);
838         },
839
840         /**
841          * Returns the minimum value in the Array.
842          *
843          * @param {Array|NodeList} array The Array from which to select the minimum value.
844          * @param {Function} comparisonFn (optional) a function to perform the comparision which determines minimization.
845          * If omitted the "<" operator will be used. Note: gt = 1; eq = 0; lt = -1
846          * @return {Mixed} minValue The minimum value
847          */
848         min: function(array, comparisonFn) {
849             var min = array[0],
850                 i, ln, item;
851
852             for (i = 0, ln = array.length; i < ln; i++) {
853                 item = array[i];
854
855                 if (comparisonFn) {
856                     if (comparisonFn(min, item) === 1) {
857                         min = item;
858                     }
859                 }
860                 else {
861                     if (item < min) {
862                         min = item;
863                     }
864                 }
865             }
866
867             return min;
868         },
869
870         /**
871          * Returns the maximum value in the Array.
872          *
873          * @param {Array|NodeList} array The Array from which to select the maximum value.
874          * @param {Function} comparisonFn (optional) a function to perform the comparision which determines maximization.
875          * If omitted the ">" operator will be used. Note: gt = 1; eq = 0; lt = -1
876          * @return {Mixed} maxValue The maximum value
877          */
878         max: function(array, comparisonFn) {
879             var max = array[0],
880                 i, ln, item;
881
882             for (i = 0, ln = array.length; i < ln; i++) {
883                 item = array[i];
884
885                 if (comparisonFn) {
886                     if (comparisonFn(max, item) === -1) {
887                         max = item;
888                     }
889                 }
890                 else {
891                     if (item > max) {
892                         max = item;
893                     }
894                 }
895             }
896
897             return max;
898         },
899
900         /**
901          * Calculates the mean of all items in the array.
902          *
903          * @param {Array} array The Array to calculate the mean value of.
904          * @return {Number} The mean.
905          */
906         mean: function(array) {
907             return array.length > 0 ? ExtArray.sum(array) / array.length : undefined;
908         },
909
910         /**
911          * Calculates the sum of all items in the given array.
912          *
913          * @param {Array} array The Array to calculate the sum value of.
914          * @return {Number} The sum.
915          */
916         sum: function(array) {
917             var sum = 0,
918                 i, ln, item;
919
920             for (i = 0,ln = array.length; i < ln; i++) {
921                 item = array[i];
922
923                 sum += item;
924             }
925
926             return sum;
927         },
928
929         //<debug>
930         _replaceSim: replaceSim, // for unit testing
931         _spliceSim: spliceSim,
932         //</debug>
933
934         /**
935          * Removes items from an array. This is functionally equivalent to the splice method
936          * of Array, but works around bugs in IE8's splice method and does not copy the
937          * removed elements in order to return them (because very often they are ignored).
938          *
939          * @param {Array} array The Array on which to replace.
940          * @param {Number} index The index in the array at which to operate.
941          * @param {Number} removeCount The number of items to remove at index.
942          * @return {Array} The array passed.
943          * @method
944          */
945         erase: erase,
946
947         /**
948          * Inserts items in to an array.
949          * 
950          * @param {Array} array The Array on which to replace.
951          * @param {Number} index The index in the array at which to operate.
952          * @param {Array} items The array of items to insert at index.
953          * @return {Array} The array passed.
954          */
955         insert: function (array, index, items) {
956             return replace(array, index, 0, items);
957         },
958
959         /**
960          * Replaces items in an array. This is functionally equivalent to the splice method
961          * of Array, but works around bugs in IE8's splice method and is often more convenient
962          * to call because it accepts an array of items to insert rather than use a variadic
963          * argument list.
964          * 
965          * @param {Array} array The Array on which to replace.
966          * @param {Number} index The index in the array at which to operate.
967          * @param {Number} removeCount The number of items to remove at index (can be 0).
968          * @param {Array} insert An optional array of items to insert at index.
969          * @return {Array} The array passed.
970          * @method
971          */
972         replace: replace,
973
974         /**
975          * Replaces items in an array. This is equivalent to the splice method of Array, but
976          * works around bugs in IE8's splice method. The signature is exactly the same as the
977          * splice method except that the array is the first argument. All arguments following
978          * removeCount are inserted in the array at index.
979          *
980          * @param {Array} array The Array on which to replace.
981          * @param {Number} index The index in the array at which to operate.
982          * @param {Number} removeCount The number of items to remove at index (can be 0).
983          * @return {Array} An array containing the removed items.
984          * @method
985          */
986         splice: splice
987     };
988
989     /**
990      * @method
991      * @member Ext
992      * @alias Ext.Array#each
993      */
994     Ext.each = ExtArray.each;
995
996     /**
997      * @method
998      * @member Ext.Array
999      * @alias Ext.Array#merge
1000      */
1001     ExtArray.union = ExtArray.merge;
1002
1003     /**
1004      * Old alias to {@link Ext.Array#min}
1005      * @deprecated 4.0.0 Use {@link Ext.Array#min} instead
1006      * @method
1007      * @member Ext
1008      * @alias Ext.Array#min
1009      */
1010     Ext.min = ExtArray.min;
1011
1012     /**
1013      * Old alias to {@link Ext.Array#max}
1014      * @deprecated 4.0.0 Use {@link Ext.Array#max} instead
1015      * @method
1016      * @member Ext
1017      * @alias Ext.Array#max
1018      */
1019     Ext.max = ExtArray.max;
1020
1021     /**
1022      * Old alias to {@link Ext.Array#sum}
1023      * @deprecated 4.0.0 Use {@link Ext.Array#sum} instead
1024      * @method
1025      * @member Ext
1026      * @alias Ext.Array#sum
1027      */
1028     Ext.sum = ExtArray.sum;
1029
1030     /**
1031      * Old alias to {@link Ext.Array#mean}
1032      * @deprecated 4.0.0 Use {@link Ext.Array#mean} instead
1033      * @method
1034      * @member Ext
1035      * @alias Ext.Array#mean
1036      */
1037     Ext.mean = ExtArray.mean;
1038
1039     /**
1040      * Old alias to {@link Ext.Array#flatten}
1041      * @deprecated 4.0.0 Use {@link Ext.Array#flatten} instead
1042      * @method
1043      * @member Ext
1044      * @alias Ext.Array#flatten
1045      */
1046     Ext.flatten = ExtArray.flatten;
1047
1048     /**
1049      * Old alias to {@link Ext.Array#clean}
1050      * @deprecated 4.0.0 Use {@link Ext.Array#clean} instead
1051      * @method
1052      * @member Ext
1053      * @alias Ext.Array#clean
1054      */
1055     Ext.clean = ExtArray.clean;
1056
1057     /**
1058      * Old alias to {@link Ext.Array#unique}
1059      * @deprecated 4.0.0 Use {@link Ext.Array#unique} instead
1060      * @method
1061      * @member Ext
1062      * @alias Ext.Array#unique
1063      */
1064     Ext.unique = ExtArray.unique;
1065
1066     /**
1067      * Old alias to {@link Ext.Array#pluck Ext.Array.pluck}
1068      * @deprecated 4.0.0 Use {@link Ext.Array#pluck Ext.Array.pluck} instead
1069      * @method
1070      * @member Ext
1071      * @alias Ext.Array#pluck
1072      */
1073     Ext.pluck = ExtArray.pluck;
1074
1075     /**
1076      * @method
1077      * @member Ext
1078      * @alias Ext.Array#toArray
1079      */
1080     Ext.toArray = function() {
1081         return ExtArray.toArray.apply(ExtArray, arguments);
1082     };
1083 })();
1084