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

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

        mixinConfig: {
            id: 'filterable'
        },

        config: {
            /**
             * @cfg {Array} filters
             * An array with filters. A filter can be an instance of Ext.util.Filter,
             * an object representing an Ext.util.Filter configuration, or a filter function.
             */
            filters: null,

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

        /**
         * @property {Boolean} dirtyFilterFn
         * A flag indicating wether the currently cashed filter function is still valid. Read-only.
         */
        dirtyFilterFn: 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.
         */
        filterFn: null,

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

        applyFilters: function(filters, collection) {
            if (!collection) {
                collection = this.createFiltersCollection();
            }

            collection.clear();

            this.filtered = false;
            this.dirtyFilterFn = true;

            if (filters) {
                this.addFilters(filters);
            }

            return collection;
        },

        createFiltersCollection: function() {
            this._filters = Ext.create('Ext.util.Collection', function(filter) {
                return filter.getId();
            });
            return this._filters;
        },

        /**
         * This method adds a filter.
         * @param {Ext.util.Sorter/Function/Object} filter Can be an instance of Ext.util.Filter,
         * an object representing an Ext.util.Filter configuration, or a filter function.
         */
        addFilter: function(filter) {
            this.addFilters([filter]);
        },

        /**
         * This method adds all the filters in a passed array.
         * @param {Array} filters An array with filters. A filter can be an instance of Ext.util.Filter,
         * an object representing an Ext.util.Filter configuration, or a filter function.
         */
        addFilters: function(filters) {
            var currentFilters = this.getFilters();
            return this.insertFilters(currentFilters ? currentFilters.length : 0, filters);
        },

        /**
         * This method adds a filter at a given index.
         * @param {Number} index The index at which to insert the filter.
         * @param {Ext.util.Sorter/Function/Object} filter Can be an instance of Ext.util.Filter,
         * an object representing an Ext.util.Filter configuration, or a filter function.
         */
        insertFilter: function(index, filter) {
            return this.insertFilters(index, [filter]);
        },

        /**
         * This method inserts all the filters in the passed array at the given index.
         * @param {Number} index The index at which to insert the filters.
         * @param {Array} filters Each filter can be an instance of Ext.util.Filter,
         * an object representing an Ext.util.Filter configuration, or a filter function.
         */
        insertFilters: function(index, filters) {
            // We begin by making sure we are dealing with an array of sorters
            if (!Ext.isArray(filters)) {
                filters = [filters];
            }

            var ln = filters.length,
                filterRoot = this.getFilterRoot(),
                currentFilters = this.getFilters(),
                newFilters = [],
                filterConfig, i, filter;

            if (!currentFilters) {
                currentFilters = this.createFiltersCollection();
            }

            // We first have to convert every sorter into a proper Sorter instance
            for (i = 0; i < ln; i++) {
                filter = filters[i];
                filterConfig = {
                    root: filterRoot
                };

                if (Ext.isFunction(filter)) {
                    filterConfig.filterFn = filter;
                }
                // 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(filter)) {
                    if (!filter.isFilter) {
                        if (filter.fn) {
                            filter.filterFn = filter.fn;
                            delete filter.fn;
                        }

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

                // If a sorter config was created, make it an instance
                filter = Ext.create('Ext.util.Filter', filterConfig);
                newFilters.push(filter);
            }

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

            this.dirtyFilterFn = true;

            if (currentFilters.length) {
                this.filtered = true;
            }

            return currentFilters;
        },

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

            var ln = filters.length,
                currentFilters = this.getFilters(),
                i, filter;

            for (i = 0; i < ln; i++) {
                filter = filters[i];

                if (typeof filter === 'string') {
                    currentFilters.each(function(item) {
                        if (item.getProperty() === filter) {
                            currentFilters.remove(item);
                        }
                    });
                }
                else if (typeof filter === 'function') {
                    currentFilters.each(function(item) {
                        if (item.getFilterFn() === filter) {
                            currentFilters.remove(item);
                        }
                    });
                }
                else {
                    if (filter.isFilter) {
                        currentFilters.remove(filter);
                    }
                    else if (filter.property !== undefined && filter.value !== undefined) {
                        currentFilters.each(function(item) {
                            if (item.getProperty() === filter.property && item.getValue() === filter.value) {
                                currentFilters.remove(item);
                            }
                        });
                    }
                }
            }

            if (!currentFilters.length) {
                this.filtered = false;
            }
        },

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

            this.filterFn = function(item) {
                var isMatch = true,
                    length = filters.length,
                    i;

                for (i = 0; i < length; i++) {
                    var filter = filters[i],
                        fn     = filter.getFilterFn(),
                        scope  = filter.getScope() || this;

                    isMatch = isMatch && fn.call(scope, item);
                }

                return isMatch;
            };

            this.dirtyFilterFn = false;
            return this.filterFn;
        },

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

        isFiltered: function(item) {
            return this.getFilters().length ? !this.getFilterFn()(item) : false;
        },

        /**
         * Returns an up to date sort function.
         * @return {Function} sortFn The sort function.
         */
        getFilterFn: function() {
            if (this.dirtyFilterFn) {
                return this.updateFilterFn();
            }
            return this.filterFn;
        }
    });