    /**
     * @private
     */
    Ext.define('Ext.mixin.Sortable', {
        extend: 'Ext.mixin.Mixin',

        requires: [
            'Ext.util.Sorter'
        ],

        mixinConfig: {
            id: 'sortable'
        },

        config: {
            /**
             * @cfg {Array} sorters
             * An array with sorters. A sorter can be an instance of Ext.util.Sorter, a string
             * indicating a property name, an object representing an Ext.util.Sorter configuration,
             * or a sort function.
             */
            sorters: null,

            /**
             * @cfg {String} defaultSortDirection
             * The default sort direction to use if one is not specified (defaults to "ASC")
             */
            defaultSortDirection: "ASC",

            /**
             * @cfg {String} sortRoot
             * The root inside each item in which the properties exist that we want to sort on.
             * This is useful for sorting records in which the data exists inside a 'data' property.
             */
            sortRoot: null
        },

        /**
         * @property {Boolean} dirtySortFn
         * A flag indicating wether the currently cashed sort function is still valid. Read-only.
         */
        dirtySortFn: false,

        /**
         * @property currentSortFn
         * This is the cached sorting function which is a generated function that calls all the
         * configured sorters in the correct order. This is a read-only property.
         */
        sortFn: null,

        /**
         * @property {Boolean} sorted
         * A read-only flag indicating if this object is sorted
         */
        sorted: false,

        applySorters: function(sorters, collection) {
            if (!collection) {
                collection = this.createSortersCollection();
            }

            collection.clear();
            this.sorted = false;

            if (sorters) {
                this.addSorters(sorters);
            }

            return collection;
        },

        createSortersCollection: function() {
            this._sorters = Ext.create('Ext.util.Collection', function(sorter) {
                return sorter.getId();
            });
            return this._sorters;
        },

        /**
         * This method adds a sorter.
         * @param {Ext.util.Sorter/String/Function/Object} sorter Can be an instance of
         * Ext.util.Sorter, a string indicating a property name, an object representing an Ext.util.Sorter
         * configuration, or a sort function.
         * @param {String} defaultDirection The default direction for each sorter in the array. Defaults
         * to the value of {@link #defaultSortDirection}. Can be either 'ASC' or 'DESC'.
         */
        addSorter: function(sorter, defaultDirection) {
            this.addSorters([sorter], defaultDirection);
        },

        /**
         * This method adds all the sorters in a passed array.
         * @param {Array} sorters An array with sorters. A sorter can be an instance of Ext.util.Sorter, a string
         * indicating a property name, an object representing an Ext.util.Sorter configuration,
         * or a sort function.
         * @param {String} defaultDirection The default direction for each sorter in the array. Defaults
         * to the value of {@link #defaultSortDirection}. Can be either 'ASC' or 'DESC'.
         */
        addSorters: function(sorters, defaultDirection) {
            var currentSorters = this.getSorters();
            return this.insertSorters(currentSorters ? currentSorters.length : 0, sorters, defaultDirection);
        },

        /**
         * This method adds a sorter at a given index.
         * @param {Number} index The index at which to insert the sorter.
         * @param {Ext.util.Sorter/String/Function/Object} sorter Can be an instance of Ext.util.Sorter,
         * a string indicating a property name, an object representing an Ext.util.Sorter configuration,
         * or a sort function.
         * @param {String} defaultDirection The default direction for each sorter in the array. Defaults
         * to the value of {@link #defaultSortDirection}. Can be either 'ASC' or 'DESC'.
         */
        insertSorter: function(index, sorter, defaultDirection) {
            return this.insertSorters(index, [sorter], defaultDirection);
        },

        /**
         * This method inserts all the sorters in the passed array at the given index.
         * @param {Number} index The index at which to insert the sorters.
         * @param {Array} sorters Can be an instance of Ext.util.Sorter, a string indicating a property name,
         * an object representing an Ext.util.Sorter configuration, or a sort function.
         * @param {String} defaultDirection The default direction for each sorter in the array. Defaults
         * to the value of {@link #defaultSortDirection}. Can be either 'ASC' or 'DESC'.
         */
        insertSorters: function(index, sorters, defaultDirection) {
            // We begin by making sure we are dealing with an array of sorters
            if (!Ext.isArray(sorters)) {
                sorters = [sorters];
            }

            var ln = sorters.length,
                direction = defaultDirection || this.getDefaultSortDirection(),
                sortRoot = this.getSortRoot(),
                currentSorters = this.getSorters(),
                newSorters = [],
                sorterConfig, i, sorter, currentSorter;

            if (!currentSorters) {
                // This will guarantee that we get the collection
                currentSorters = this.createSortersCollection();
            }

            // We first have to convert every sorter into a proper Sorter instance
            for (i = 0; i < ln; i++) {
                sorter = sorters[i];
                sorterConfig = {
                    direction: direction,
                    root: sortRoot
                };

                // If we are dealing with a string we assume it is a property they want to sort on.
                if (typeof sorter === 'string') {
                    currentSorter = currentSorters.get(sorter);

                    if (!currentSorter) {
                        sorterConfig.property = sorter;
                    } else {
                        if (defaultDirection !== undefined) {
                            currentSorter.setDirection(defaultDirection);
                        } else {
                            // If we already have a sorter for this property we just toggle its direction.
                            currentSorter.toggle();
                        }
                        continue;
                    }
                }
                // If it is a function, we assume its a sorting function.
                else if (Ext.isFunction(sorter)) {
                    sorterConfig.sorterFn = sorter;
                }
                // If we are dealing with an object, we assume its a Sorter configuration. In this case
                // we create an instance of Sorter passing this configuration.
                else if (Ext.isObject(sorter)) {
                    if (!sorter.isSorter) {
                        if (sorter.fn) {
                            sorter.sorterFn = sorter.fn;
                            delete sorter.fn;
                        }

                        sorterConfig = Ext.apply(sorterConfig, sorter);
                    }
                    else {
                        newSorters.push(sorter);
                        if (!sorter.getRoot()) {
                            sorter.setRoot(sortRoot);
                        }
                        continue;
                    }
                }
                // Finally we get to the point where it has to be invalid
                // <debug>
                else {
                    Ext.Logger.warn('Invalid sorter specified:', sorter);
                }
                // </debug>

                // If a sorter config was created, make it an instance
                sorter = Ext.create('Ext.util.Sorter', sorterConfig);
                newSorters.push(sorter);
            }

            // Now lets add the newly created sorters.
            for (i = 0, ln = newSorters.length; i < ln; i++) {
                currentSorters.insert(index + i, newSorters[i]);
            }

            this.dirtySortFn = true;

            if (currentSorters.length) {
                this.sorted = true;
            }
            return currentSorters;
        },

        /**
         * This method removes a sorter.
         * @param {Ext.util.Sorter/String/Function/Object} sorter Can be an instance of Ext.util.Sorter,
         * a string indicating a property name, an object representing an Ext.util.Sorter configuration,
         * or a sort function.
         */
        removeSorter: function(sorter) {
            return this.removeSorters([sorter]);
        },

        /**
         * This method removes all the sorters in a passed array.
         * @param {Array} sorters Each value in the array can be a string (property name),
         * function (sorterFn) or {@link Ext.util.Sorter Sorter} instance.
         */
        removeSorters: function(sorters) {
            // We begin by making sure we are dealing with an array of sorters
            if (!Ext.isArray(sorters)) {
                sorters = [sorters];
            }

            var ln = sorters.length,
                currentSorters = this.getSorters(),
                i, sorter;

            for (i = 0; i < ln; i++) {
                sorter = sorters[i];

                if (typeof sorter === 'string') {
                    currentSorters.removeAtKey(sorter);
                }
                else if (typeof sorter === 'function') {
                    currentSorters.each(function(item) {
                        if (item.getSorterFn() === sorter) {
                            currentSorters.remove(item);
                        }
                    });
                }
                else if (sorter.isSorter) {
                    currentSorters.remove(sorter);
                }
            }

            if (!currentSorters.length) {
                this.sorted = false;
            }
        },

        /**
         * This updates the cached sortFn based on the current sorters.
         * @return {Function} sortFn The generated sort function.
         * @private
         */
        updateSortFn: function() {
            var sorters = this.getSorters().items;

            this.sortFn = function(r1, r2) {
                var ln = sorters.length,
                    result, i;

                // We loop over each sorter and check if r1 should be before or after r2
                for (i = 0; i < ln; i++) {
                    result = sorters[i].sort.call(this, r1, r2);

                    // If the result is -1 or 1 at this point it means that the sort is done.
                    // Only if they are equal (0) we continue to see if a next sort function
                    // actually might find a winner.
                    if (result !== 0) {
                        break;
                    }
                }

                return result;
            };

            this.dirtySortFn = false;
            return this.sortFn;
        },

        /**
         * Returns an up to date sort function.
         * @return {Function} sortFn The sort function.
         */
        getSortFn: function() {
            if (this.dirtySortFn) {
                return this.updateSortFn();
            }
            return this.sortFn;
        },

        /**
         * This method will sort an array based on the currently configured {@link #sorters}.
         * @param {Array} data The array you want to have sorted
         * @return {Array} data The array you passed after it is sorted
         */
        sort: function(data) {
            Ext.Array.sort(data, this.getSortFn());
            return data;
        },

        /**
         * This method returns the index that a given item would be inserted into a given array based
         * on the current sorters.
         * @param {Array} items The array that you want to insert the item into.
         * @param {Mixed} item The item that you want to insert into the items array.
         * @returns {Number} index The index for the given item in the given array based on the current sorters.
         */
        findInsertionIndex: function(items, item, sortFn) {
            var start = 0,
                end   = items.length - 1,
                sorterFn = sortFn || this.getSortFn(),
                middle,
                comparison;

            while (start <= end) {
                middle = (start + end) >> 1;
                comparison = sorterFn(item, items[middle]);
                if (comparison >= 0) {
                    start = middle + 1;
                } else if (comparison < 0) {
                    end = middle - 1;
                }
            }

            return start;
        }
    });