    Ext.define('Ext.event.ListenerStack', {

        currentOrder: 'current',

        length: 0,

        constructor: function() {
            this.listeners = {
                before: [],
                current: [],
                after: []
            };

            this.lateBindingMap = {};

            return this;
        },

        add: function(fn, scope, options, order) {
            var lateBindingMap = this.lateBindingMap,
                listeners = this.getAll(order),
                i = listeners.length,
                bindingMap, listener, id;

            if (typeof fn == 'string' && scope.isIdentifiable) {
                id = scope.getId();

                bindingMap = lateBindingMap[id];

                if (bindingMap) {
                    if (bindingMap[fn]) {
                        return false;
                    }
                    else {
                        bindingMap[fn] = true;
                    }
                }
                else {
                    lateBindingMap[id] = bindingMap = {};
                    bindingMap[fn] = true;
                }
            }
            else {
                if (i > 0) {
                    while (i--) {
                        listener = listeners[i];

                        if (listener.fn === fn && listener.scope === scope) {
                            listener.options = options;
                            return false;
                        }
                    }
                }
            }

            listener = this.create(fn, scope, options, order);

            if (options && options.prepend) {
                delete options.prepend;
                listeners.unshift(listener);
            }
            else {
                listeners.push(listener);
            }

            this.length++;

            return true;
        },

        getAt: function(index, order) {
            return this.getAll(order)[index];
        },

        getAll: function(order) {
            if (!order) {
                order = this.currentOrder;
            }

            return this.listeners[order];
        },

        count: function(order) {
            return this.getAll(order).length;
        },

        create: function(fn, scope, options, order) {
            return {
                stack: this,
                fn: fn,
                firingFn: false,
                boundFn: false,
                isLateBinding: typeof fn == 'string',
                scope: scope,
                options: options || {},
                order: order
            };
        },

        remove: function(fn, scope, order) {
            var listeners = this.getAll(order),
                i = listeners.length,
                isRemoved = false,
                lateBindingMap = this.lateBindingMap,
                listener, id;

            if (i > 0) {
                // Start from the end index, faster than looping from the
                // beginning for "single" listeners,
                // which are normally LIFO
                while (i--) {
                    listener = listeners[i];

                    if (listener.fn === fn && listener.scope === scope) {
                        listeners.splice(i, 1);
                        isRemoved = true;
                        this.length--;

                        if (typeof fn == 'string' && scope.isIdentifiable) {
                            id = scope.getId();

                            if (lateBindingMap[id] && lateBindingMap[id][fn]) {
                                delete lateBindingMap[id][fn];
                            }
                        }
                        break;
                    }
                }
            }

            return isRemoved;
        }
    });