In case Flash no longer exists; a copy of this site is included in the Flashpoint archive's "ultimate" collection.

Dead Code Preservation :: Archived AS3 works from wonderfl.net

ListenerProxy

The ListenerProxy-Class is an easy to use controller for ActionScript 3 EventListeners. It helps you to easily keep track of all the Listeners you register onto an Object. You are able to
• Add/Remove single Listener to single Object
• Add/Remove single Listener to multiple Objects
• Add/Remove multiple Listeners to single Object
• Add/Remove multiple Listeners to multiple Objects
with a single line if you like :) I prefer using more than one line for better readability.
But there's even more:
• Check if an object has a specified Listener
• Check if an object has a specified Listener, linked to a specified Function (remember, you're able to register multiple Functions to one single Event in ActionScript by default)
• Print a list of all registered EventListeners of a specified Object

Does not work standalone. Instructions included in the code.
/** @private
    ///// -- DANIELBUNTE.DE -- /////

    [CLASS]       :: de.danielbunte.utils.listeners.ListenerProxy
    [CREATED]     :: v.1-alpha 12th February 2009

    [VERSION]     :: v3.0.0-stable

    [MODIFIED]    :: 17th April    2012

    [AUTHOR]     :: Daniel Bunte, Graf-Ludolf-Str. 23, D-31515, Wunstorf    (2010)
    [AUTHOR]     :: Daniel Bunte, Am Heisterholz 2, D-31535, Neustadt am Ruebenberge (2011)
    [AUTHOR]     :: Daniel Bunte, Reetzer Str. 10a, D-31515, Wunstorf (2011)

    /////


    [ D E S C R I P T I O N ]

    This is the ListenerProxy-class which keeps track of Listeners registred on an Object

    END -::- [ D E S C R I P T I O N ]

    /////


    [ C H A N G E L O G ]

    ::12th February 2009 { created the class and defined basic methods

                           [v.1-alpha]
                         }


    ::13th February 2009 { completed class with useful functions

                           [v.1-beta]
                         }


    ::19th February 2009 { moved class from com.meinebuntetuete to de.danielbunte for future use; tested functionality
                           of the class

                           [v1.0-stable]
                         }


    ::23rd February 2009 { modified hasListener- & search-methods, so that the function-param could be null

                           [v1.1-stable]
                         }


    ::16th April    2009 { modified the methods addListener and removeListener so that either a String or even an Array
                           could be given as strListenerType-parameter

                           [v1.2-stable]
                         }


    ::29th April    2009 { modified the methods addListener and removeListener so that either a String or even an Array
                           could be given as objToAddTo-parameter

                           [v1.3-stable]
                         }


    ::30th May        2009 { changed all uints to ints, because of performance; added VERSION trace

                           [v1.3.1-stable]
                         }

    
    ::01st June        2009 { removed a bug that throwed an Error if addListener recieved an Array as Params

                           [v1.3.2-stable]
                         }

    
    ::22nd June        2009 { made just a few code- & syntax-improvements

                           [v1.3.3-stable]
                         }

    
    ::06th August    2009 { fixed bug with adding/removing multiple Listeners to multiple Objects (e.g.: ListenerProxy.getInstance().
                           addListener([this.objFoo, this.objBar], [Event.COMPLETE, ProgressEvent.PROGRESS], this.loadHandler);)
                           also removed a duplicate if(!objCurrentObject.hasOwnProperty('addEventListener'))
                                                    {
                                                        throw new Error(ListenerProxy.NO_ABILITY_ERROR + ' => ' + objToAddTo);
                                                    }

                            [v1.3.4-stable]
                         }


    ::18th August    2009 { made some formatting- & performance-improvements; removed asterisks as type-definition and
                           replaced with Object-type; added method to trace all listeners (getListenersListing), so removed
                           the T O D O; commented out the public static function self(); formatted the C H A N G E L O G

                           [v1.4.0-stable]
                         }
    
    ::10th January    2010 { now using Vectors to store Listeners, instead of Arrays - that's a lot faster;
                           completely removed the static method "self", use getInstance instead!;
                           added ASDocs-like comments;
                           optimized and shortened if-expressions, now using some switch-statements;
                           instead of using Arrays for multiple Listeners/Objects, you can also pass a
                           Vector of type Object/Sprite/Whatever for your Objects and of type String
                           for the EventListener-Types (it's faster, believe me!);
                           if an object doesn't have any more Listeners, it is completely remove from
                           Dictionary, this should happen since v1.0-stable but was obviously a bug.;
                           Improved and extended Error-Messages

                           [v2.0.0-stable]
                         }

    ::12th February    2010 { added license-message

                           [v2.0.0-stable]
                         }

    ::18th February    2010 { Completely removed Arrays - these are now directly converted into Vector by
                           using Vector.<Object>(myArr);

                           [v2.0.1-stable]
                         }

    ::25th February    2010 { Arrays can't be converted into a Vector by simply doing vec = myArr as Vector.<T>,
                           so I had to add the a again. This is obviously a bugfix :)

                           [v2.0.2-stable]
                         }
    
    ::01st April    2011 { fixed a bug in hasListener that always returned true (wrong comparison between
                           result >= int(0 - 1) what should be [and now it is] result !== int(0 - 1))

                           [v2.0.3-stable]
                         }

    ::27th July        2011 { pop() now supports multiple objects to be popped at once, pop() doesn't return Objects anymore
                           optimized loops to while(++i !== len) instead of for(; i < len; ++i);
                           some other minor optimizations

                           [v2.1.0-stable]
                         }
                         
    ::27th July        2011 { set blnRemoveAllListeners of pop() to true by default

                           [v2.1.1-stable]
                         }

    ::17th April    2012 { added ListenerProxyEntry as an internal class, instead of using an untyped Object
                           Arrays are now converted into Vector.<String> automatically, so we only need one typed variable for the add/remove methods
                           cleaned up the code for open source relase

                           [v3.0.0-stable]
                         }

    END -::- [ C H A N G E L O G ]

    /////

*/

package de.danielbunte.utils.listeners
{
    import flash.events.TimerEvent;

    import flash.net.URLLoader;
    import flash.net.URLRequest;

    import flash.utils.Dictionary;
    import flash.utils.describeType;
    import flash.utils.getQualifiedClassName;
    import flash.utils.getQualifiedSuperclassName;
    import flash.utils.Timer;


    public class ListenerProxy
    {
        private static var      objClassInstance             :ListenerProxy     ;

        private var             dictObjects                  :Dictionary        ,    //all the objects will be stored here
                                intNumObjects                :int               ;    //stores the Number of Objects within the Dictionary-Object

        private static const    NO_ABILITY_ERROR             :String = 'ListenerProxy:: Object has no ability to add an EventListener!',
                                NOT_INITIALIZABLE            :String = 'ListenerProxy:: no direct initialization! user ListenerProxy.getInstance() instead.',
                                SYNTAX_ERROR                 :String = 'ListenerProxy:: Syntax Error while adding or removing Listener, or popping Object!',
                                USAGE_INSTRUCTIONS           :String = 'ListenerProxy:: Usage Instructions following\n\n' +
                                                                       'You can avoid this message by using ListenerProxy.getInstance(false) when you use this class the first time in your code.\n\n' +
                                                                       'This ListenerProxy-Class helps you to easily keep track of all the Listeners you register onto an Object. You are able to\n' +
                                                                       ' • Add/Remove single Listener to single Object\n' + 
                                                                       ' • Add/Remove single Listener to multiple Objects\n' + 
                                                                       ' • Add/Remove multiple Listeners to single Object \n' + 
                                                                       ' • Add/Remove multiple Listeners to multiple Objects\n' + 
                                                                       'with a single line if you like :) I prefer using more than one line for better readability.\n' + 
                                                                       'But there\'s even more:\n' + 
                                                                       ' • Check if an object has a specified Listener\n' + 
                                                                       ' • Check if an object has a specified Listener, linked to a specified Function (remember, you\'re able to register multiple Functions to one single Event in ActionScript by default)\n' + 
                                                                       ' • Print a list of all registered EventListeners of a specified Object\n\n' + 
                                                                       'Case 1 - You want to add one single EventListener to one single Object:\n' + 
                                                                       'ListenerProxy.getInstance.addListener(mySprite, MouseEvent.MOUSE_OVER, this.mouseHandler);\n' + 
                                                                       '\tThis is the same as:\n' + 
                                                                       '\tmySprite.addEventListener(MouseEvent.MOUSE_OVER, this.mouseHandler);\n\n' + 
                                                                       'Case 2 - You want to add one single EventListener to multiple Objects:\n' + 
                                                                       'ListenerProxy.getInstance.addListener([mySprite_1, mySprite_2, mySprite_3], MouseEvent.MOUSE_OVER, this.mouseHandler);\n' +
                                                                       '\tThis is the same as:\n' + 
                                                                       '\tmySprite_1.addEventListener(MouseEvent.MOUSE_OVER, this.mouseHandler);\n' + 
                                                                       '\tmySprite_2.addEventListener(MouseEvent.MOUSE_OVER, this.mouseHandler);\n' + 
                                                                       '\tmySprite_3.addEventListener(MouseEvent.MOUSE_OVER, this.mouseHandler);\n\n' + 
                                                                       'Case 3 - You want to add multiple EventListeners to one single Object:\n' + 
                                                                       'ListenerProxy.getInstance().addListener(mySprite, [MouseEvent.MOUSE_OVER, MouseEvent.MOUSE_OUT, MouseEvent.CLICK], this.mouseHandler);\n' + 
                                                                       '\t\tN O T E: I always like to do some formatting like this:\n' + 
                                                                       '\t\tListenerProxy.getInstance().addListener(mySprite,\n' + 
                                                                       '\t\t                                                  [\n' + 
                                                                       '\t\t                                                   MouseEvent.MOUSE_OVER,\n' + 
                                                                       '\t\t                                                   MouseEvent.MOUSE_OUT,\n' + 
                                                                       '\t\t                                                   MouseEvent.CLICK\n' + 
                                                                       '\t\t                                                  ],\n' + 
                                                                       '\t\t                                                  this.mouseHandler);\n' + 
                                                                       '\t\tI like it that way and think it\'s better readable, but that\'s just a tip.\n' + 
                                                                       '\tThe above Lines are the same like this:\n' + 
                                                                       '\tmySprite.addEventListener(MouseEvent.MOUSE_OVER, this.mouseHandler);\n' + 
                                                                       '\tmySprite.addEventListener(MouseEvent.MOUSE_OUT,  this.mouseHandler);\n' + 
                                                                       '\tmySprite.addEventListener(MouseEvent.CLICK,      this.mouseHandler);\n\n' + 
                                                                       'Case 4 - You want to add multiple EventListeners to multiple Objects:\n' + 
                                                                       'ListenerProxy.getInstance.addListener([mySprite_1, mySprite_2, mySprite_3], [MouseEvent.MOUSE_OVER, MouseEvent.MOUSE_OUT, MouseEvent.CLICK], this.mouseHandler);\n' + 
                                                                       '\tThis is the same as:\n' +
                                                                       '\tmySprite_1.addEventListener(MouseEvent.MOUSE_OVER, this.mouseHandler);\n' + 
                                                                       '\tmySprite_1.addEventListener(MouseEvent.MOUSE_OUT,  this.mouseHandler);\n' + 
                                                                       '\tmySprite_1.addEventListener(MouseEvent.CLICK,      this.mouseHandler);\n' +
                                                                       '\tmySprite_2.addEventListener(MouseEvent.MOUSE_OVER, this.mouseHandler);\n' + 
                                                                       '\tmySprite_2.addEventListener(MouseEvent.MOUSE_OUT,  this.mouseHandler);\n' + 
                                                                       '\tmySprite_2.addEventListener(MouseEvent.CLICK,      this.mouseHandler);\n' +
                                                                       '\tmySprite_3.addEventListener(MouseEvent.MOUSE_OVER, this.mouseHandler);\n' + 
                                                                       '\tmySprite_3.addEventListener(MouseEvent.MOUSE_OUT,  this.mouseHandler);\n' + 
                                                                       '\tmySprite_3.addEventListener(MouseEvent.CLICK,      this.mouseHandler);\n\n' + 
                                                                       'Instead of using an Array for multiple Listeners/Objects by using the braces [], you can also pass a Vector with type Object as parameter.\n' + 
                                                                       'That\'d look like this:\n' + 
                                                                       'ListenerProxy.getInstance().addListener(new <Object>[\n' + 
                                                                       '                                                     mySprite_1,\n' + 
                                                                       '                                                     mySprite_2,\n' + 
                                                                       '                                                     mySprite_3\n' + 
                                                                       '                                                    ],\n' + 
                                                                       '                                        new <String>[\n' + 
                                                                       '                                                     MouseEvent.MOUSE_OVER,\n' + 
                                                                       '                                                     MouseEvent.MOUSE_OUT,\n' + 
                                                                       '                                                     MouseEvent.CLICK\n' + 
                                                                       '                                                    ],\n' + 
                                                                       '                                        this.mouseHandler);\n' + 
                                                                       'This is the recommended way - of course the array-version is easier to type and to teach.\n' + 
                                                                       'For further information on Vector, see this page and especially the user-comments: http://livedocs.adobe.com/flex/3/langref/Vector.html\n\n' +
                                                                       'ListenerProxy:: End of Usage Instructions',
                              VERSION                        :String = 'ListenerProxy:: v3.0.0-stable (17th July 2012) [© by Daniel Bunte - danielbunte.de] | Also visit my agency\'s website http://badnoob.com! We do excellent Flashwork and powerful standalone Server-applications';


        /** @private **/
        public function ListenerProxy(internalClass:InternalListenerProxy):void //Singleton is used!
        {
            if(!internalClass)
            {
                throw new Error(ListenerProxy.NOT_INITIALIZABLE);
            }
    
            this.dictObjects = new Dictionary();
        }


// -- PUBLIC STATIC METHODS -- //
        /**
         * Returns an Instance of the Class.
         * 
         * <p>We use a Singleton-Pattern, so only one instance of this class is created at all. We
         * only need one, or do you need more? :D</p>
         * 
         * @param    blnPrintInstructions    If set to true(default) the Usage-Instructions will be traced when the class is initialized.
         * 
         * @return    An instance of this Class
         */
        public static function getInstance(blnPrintInstructions:Boolean = true):ListenerProxy
        {
            if(!ListenerProxy.objClassInstance)
            {
                ListenerProxy.objClassInstance = new ListenerProxy(new InternalListenerProxy());
                trace(ListenerProxy.VERSION + ((blnPrintInstructions) ? '\n'+ ListenerProxy.USAGE_INSTRUCTIONS : ''));
            }

            return ListenerProxy.objClassInstance;
        }


// -- PUBLIC METHODS -- //
        /**
         * Adds one single or multiple Listeners to one single or multiple Objects.
         * 
         * <p>See Usage Instructions for further information.</p>
         * 
         * @param    objToAddTo        The Object(s) you want to add Listeners to. Can either be an Array, an Object or a Vector.
         * @param    strListenerType    The Listener(s) you want to add to the Object(s). Can either be an Array, a String or a Vector.
         * @param    funcCallback    The EventListener's Handler-Method.
         * 
         * @return    void
         */
        public function addListener(objToAddTo:Object, objListenerType:Object, funcCallback:Function):void
        {
            var    intNewListenerIndex    :int                        ,
                   intMinusOne            :int                = -1    ,
                   intObjectLen           :int                        ,
                   intTypeLen             :int                        ,
                   i                      :int                = -1    ,
                   j                      :int                        ,
                   objCurrentObject       :Object                     ,
                   vecObjectIterator      :Vector.<Object>            ,
                   vecTypeIterator        :Vector.<String>            ;

            switch(true)
            {
                case (objListenerType is String) :
                {
                    vecTypeIterator = new <String>[objListenerType as String];
                }; break;

                case (objListenerType is Vector.<String>) :
                {
                    vecTypeIterator = objListenerType as Vector.<String>;
                }; break;
                
                case (objListenerType is Array):
                {
                    vecTypeIterator = Vector.<String>(objListenerType);
                }; break;
                
                default :
                {
                    throw new Error(ListenerProxy.SYNTAX_ERROR + ' => ' + objListenerType + '('+ getQualifiedClassName(objToAddTo) +'.'+ getQualifiedSuperclassName(objToAddTo) +')'+ describeType(objToAddTo));
                };
            }


            switch(true)
            {
                case (objToAddTo is Vector.<Object>) :
                {
                    vecObjectIterator = objToAddTo as Vector.<Object>;
                }; break;

                case (objToAddTo is Array) :
                {
                    vecObjectIterator = Vector.<Object>(objToAddTo);
                }; break;

                case (objToAddTo is Object) :
                {
                    vecObjectIterator = new <Object>[objToAddTo];
                }; break;

                default :
                {
                    throw new Error(ListenerProxy.SYNTAX_ERROR + ' => ' + objToAddTo + '('+ getQualifiedClassName(objToAddTo) +'.'+ getQualifiedSuperclassName(objToAddTo) +')'+ describeType(objToAddTo));
                };
            }


            intObjectLen    = vecObjectIterator.length;
            intTypeLen        = vecTypeIterator.length;
            
            while(++i !== intObjectLen)
            {
                objCurrentObject = vecObjectIterator[i];
                if(this.dictObjects[objCurrentObject] === undefined) //if we don't have this object in dictionary, add it
                {
                    this.push(objCurrentObject);
                }
                
                intNewListenerIndex = this.dictObjects[objCurrentObject].length;
                
                j = intMinusOne;
                while(++j !== intTypeLen)
                {
                    this.dictObjects[objCurrentObject][intNewListenerIndex] = new ListenerProxyEntry(vecTypeIterator[j], funcCallback); //push into dictionary
                    objCurrentObject.addEventListener(vecTypeIterator[j], funcCallback); //finally add the listener to the object

                    ++intNewListenerIndex;
                }
            }
        }


                /**
         * Creates a String-List of all registered EventListeners of the given Object.
         * 
         * <p>If no Object is given as parameter, the Listeners of all Objects are traced.</p>
         * 
         * @param    objToList    The Object you want to see the list of.
         * @param    blnTrace    If set to true, the created String will be put out via trace. Default is false.
         * @param    blnNoReturn    If set to true, the created String isn't returned. Default is false.
         * 
         * @return    List with all Objects and all their EventListeners, if blnNoReturn is set to false.
         */
        public function getListenersListing(objToList:Object = null, blnTrace:Boolean = false, blnNoReturn:Boolean = false):String
        {
            var    blnObjectGiven        :Boolean = (objToList)    ,
                   intLengthListeners    :int                      ,
                   i                     :int                      ,
                   objCurrentObject      :Object                   ,
                   strToReturn           :String                   ,
                   strCurrent            :String                   ;

            if(blnTrace === false && blnNoReturn === true) return null;

            createListing:    //label to be broken by an inner loop
            {
            
                for(objCurrentObject in this.dictObjects)
                {
                    /**
                     * If there's an Object given as parameter, just overwrite the
                     * current object with that one, so we don't need to trace the
                     * whole dictionary.
                     */
                    if(blnObjectGiven    === true
                    && objCurrentObject !== objToList)
                    {
                        if(this.dictObjects[objToList] === undefined) return null;

                        objCurrentObject = objToList;
                    }
    
                    intLengthListeners = this.dictObjects[objCurrentObject].length;

                    i = -1;
                    while(++i !== intLengthListeners)
                    {
                        strCurrent = 'ListenerProxy::getListenersListing => '+ objCurrentObject +'\tlistener:\t\t\t'+ this.dictObjects[objCurrentObject][i].type +'\t\t\tcallback: '+ getQualifiedClassName(this.dictObjects[objCurrentObject][i].callback) +'.'+ this.dictObjects[objCurrentObject][i].callback +'['+ this.dictObjects[objCurrentObject][i].callback.length +' parameter]';
    
                        if(blnNoReturn === false)
                        {
                            strToReturn += strCurrent;
                        }
    
                        if(blnTrace === true)
                        {
                            trace(strCurrent);
                        }
                    }
                    
                    /**
                     * If an Object is given, we can break the whole loop here.
                     */
                    if(blnObjectGiven === true)
                    {
                        break createListing;
                    }

                }// end of for(objCurrentObject in this.dictObjects)
            }//end of createListing:

            if(blnNoReturn === false)
            {
                return strToReturn;
            }

            return null; //the script will never get here, yeah, you're right, but we need this, because otherwise we'll get an error from the complier saying that the method doesn't return any value
        }


        /**
         * Checks an Object for a listener-type.
         * 
         * <p>If you want to know if the specified Object already has a Listener of the specified type "strListenerType"
         * registered, you can use this method. You can also check if a specified Function is registered to that Event.
         * This is useful, because you're able to register multiple Functions with an Event. This of course only works
         * correct if you use the ListenerProxy-Class instead of ActionScript's addEventListener-Function.</p>
         * 
         * @param    objToAsk            This is the object we want to check.
         * @param    strListenerType        This is the Type of Event you want to check for.
         * @param    funcCallback        This is the eventHandler-Function you want to check for.
         * 
         * @return    Returns true if the Listener exists, otherwise returns false. Also returns false if the Object isn't registered within the ListenerProxy.
         */
        public function hasListener(objToAsk:Object, strListenerType:String, funcCallback:Function = null):Boolean
        {
            if(this.dictObjects[objToAsk] === undefined) return false; //if we don't have this object in dictionary, return

            if(this.search(objToAsk, strListenerType, funcCallback) !== -1) return true;

            return false;
        }


        /**
         * Deletes the specified Object, Array or Vector.<Object> from the Dictionary.
         * 
         * <p>Caution! Doesn't work like Array.pop()!
         * this function just removes the specified
         * Objects from the Dictionary. You can't do just a pop(), because the Class is not
         * really indexed, so you have to specifiy the object you want to remove.</p>
         * 
         * @param    objToPop                The object you want to remove from Dictionary. Can Either be an Object, Array or Vector.<Object> (to remove multiple Objects at once).
         * @param    blnRemoveAllListeners    If set to true, all registered Listeners of all the Objects given as objToPop will be deleted. Default is true.
         */
        public function pop(objToPop:Object, blnRemoveAllListeners:Boolean = true):void
        {
            if(objToPop === null) return;

            var intIndexOfListener        :int                                    ,
                intMinusOne               :int                            = -1    ,
                intObjectLen              :int                                    ,
                intListenerLen            :int                                    ,
                intZero                   :int                                    ,
                i                         :int                            = -1    ,
                j                         :int                            = -1    ,
                objCurrentEntry           :ListenerProxyEntry                     ,
                objCurrentObject          :Object                                 ,
                vecObjectIterator         :Vector.<Object>                        ,
                vecListeners              :Vector.<ListenerProxyEntry>            ;
            
            switch(true)
            {
                case (objToPop is Object) :
                {
                    vecObjectIterator = new <Object>[objToPop];
                }; break;

                case (objToPop is Vector.<Object>) :
                {
                    vecObjectIterator    = objToPop as Vector.<Object>;
                }; break;
                
                case (objToPop is Array) :
                {
                    vecObjectIterator    = Vector.<Object>(objToPop);
                }; break;
                
                default :
                {
                    throw new Error(ListenerProxy.SYNTAX_ERROR + ' => ' + objToPop + '('+ getQualifiedClassName(objToPop) +'.'+ getQualifiedSuperclassName(objToPop) +')'+ describeType(objToPop));
                };
            }
            
            
            intObjectLen    = vecObjectIterator.length;

            while(++i !== intObjectLen)
            {
                objCurrentObject = vecObjectIterator[i];

                if(this.dictObjects[objCurrentObject] === undefined) //if we don't have this object in dictionary, continue
                {
                    continue;
                }

                if(blnRemoveAllListeners === true)
                {
                    vecListeners    = this.dictObjects[objCurrentObject];
                    intListenerLen    = vecListeners.length;
                    j = intMinusOne;
                    while(++j !== intListenerLen)
                    {
                        objCurrentEntry = vecListeners[intZero];
                        this.internalRemoveListener(objCurrentObject, objCurrentEntry.type, objCurrentEntry.callback, intZero, false);
                    }
                }
                
                delete this.dictObjects[objCurrentObject];

                --this.intNumObjects;
            }
        }


        /**
         * Appends a the given Object to the Dictionary.
         * 
         * @param    objToPush    The Object that you want to append to the Dictionary
         * 
         * @return    void
         */
        public function push(objToPush:Object):void
        {
            if(objToPush.hasOwnProperty('addEventListener') === false) //if the object itself doesn't have the ability to add an eventlistener, don't add to dictionary
            {
                throw new Error(ListenerProxy.NO_ABILITY_ERROR + ' => ' + objToPush + '('+ getQualifiedClassName(objToPush) +'.'+ getQualifiedSuperclassName(objToPush) +')'+ describeType(objToPush));
            }

            if(this.dictObjects[objToPush] === undefined) //if it doesn't exist already
            {
                this.dictObjects[objToPush] = new<ListenerProxyEntry>[];
                ++this.intNumObjects;
            }
        }


        /**
         * Removes one single or multiple Listeners from one single or multiple Objects.
         * 
         * <p>If there are no more EventListeners on a Object, it is removed from Dictionary.
         * See Usage Instructions for further information.</p>
         * 
         * @param    objToRemoveFrom        The Object(s) you want remove Listeners from. Can either be an Array, an Object or a Vector.
         * @param    strListenerType        The Listener(s) you want to remove from the Object(s). Can either be an Array, a String or a Vector.
         * @param    funcCallback        The EventListener's Handler-Method.
         * 
         * @return    void
         */
        public function removeListener(objToRemoveFrom:Object, strListenerType:Object, funcCallback:Function):void
        {
            var intIndexOfListener    :int                        ,
                intMinusOne           :int                = -1    ,
                intObjectLen          :int                        ,
                intTypeLen            :int                        ,
                i                     :int                = -1    ,
                j                     :int                        ,
                objCurrentObject      :Object                     ,
                vecObjectIterator     :Vector.<Object>            ,
                vecTypeIterator       :Vector.<String>            ;

            switch(true)
            {
                case (strListenerType is String) :
                {
                    vecTypeIterator = new <String>[strListenerType as String];
                }; break;

                case (strListenerType is Vector.<String>) :
                {
                    vecTypeIterator = strListenerType as Vector.<String>;
                }; break;
                
                case (strListenerType is Array) :
                {
                    vecTypeIterator = Vector.<String>(strListenerType);
                }; break;
                
                default :
                {
                    throw new Error(ListenerProxy.SYNTAX_ERROR + ' => ' + strListenerType + '('+ getQualifiedClassName(objToRemoveFrom) +'.'+ getQualifiedSuperclassName(objToRemoveFrom) +')'+ describeType(objToRemoveFrom));
                };
            }


            switch(true)
            {
                case (objToRemoveFrom is Object) :
                {
                    vecObjectIterator = new <Object>[objToRemoveFrom];
                }; break;

                case (objToRemoveFrom is Vector.<Object>) :
                {
                    vecObjectIterator = objToRemoveFrom as Vector.<Object>;
                }; break;
                
                case (objToRemoveFrom is Array) :
                {
                    vecObjectIterator = Vector.<Object>(objToRemoveFrom);
                }; break;
                
                default :
                {
                    throw new Error(ListenerProxy.SYNTAX_ERROR + ' => ' + objToRemoveFrom + '('+ getQualifiedClassName(objToRemoveFrom) +'.'+ getQualifiedSuperclassName(objToRemoveFrom) +')'+ describeType(objToRemoveFrom));
                };
            }

            intObjectLen    = vecObjectIterator.length;
            intTypeLen        = vecTypeIterator.length;

            while(++i !== intObjectLen)
            {
                objCurrentObject = vecObjectIterator[i];

                if(this.dictObjects[objCurrentObject] === undefined) //if we don't have this object in dictionary, return
                {
                    continue;
                }

                j = intMinusOne;
                while(++j !== intTypeLen)
                {
                    intIndexOfListener = this.search(objCurrentObject, vecTypeIterator[j], funcCallback)
                    if(intIndexOfListener === intMinusOne) continue; //if the listener couldn't be found, continue with next one

                    this.internalRemoveListener(objCurrentObject, vecTypeIterator[j], funcCallback, intIndexOfListener);
                }
            }
        }


// -- PRIVATE METHODS -- //
        /** @private **/
        private function internalRemoveListener(objToRemoveFrom:Object, strListenerType:String, funcCallback:Function, intIndexOfListener:int, blnAutoDeleteFromDictionary:Boolean = true):void
        {
            //first remove the listener from the object itself
            var objCurrentEntry    :ListenerProxyEntry        = this.dictObjects[objToRemoveFrom][intIndexOfListener],
                strType            :String                    = objCurrentEntry.type,
                funcCallback       :Function                  = objCurrentEntry.callback;

            if(objToRemoveFrom.hasEventListener(strType) === true) //secure that the object has this listener
            {
                objToRemoveFrom.removeEventListener(strType, funcCallback);
            }

            //void variables
            strType                = null;
            //void the listener
            objCurrentEntry.destruct();
            objCurrentEntry            = null;
            this.dictObjects[objToRemoveFrom].splice(intIndexOfListener, 1);

            if(blnAutoDeleteFromDictionary === true && this.dictObjects[objToRemoveFrom].length === 0)
            {
                this.pop(objToRemoveFrom);
            }
        }


        /** @private **/
        private function search(objToSearchIn:Object, strListenerType:String, callback:Function = null):int
        {
            if(this.intNumObjects === 0) //no objects in dictionary, so return -1
            {
                return -1;
            }

            if(this.dictObjects[objToSearchIn] === undefined) //current object is not in dictionary, so return -1
            {
                return -1;
            }

            var i                      :int = -1                                        ,
                intLength              :int = this.dictObjects[objToSearchIn].length    ,
                intCurrentTypeCount    :int                                             ,
                objCurrentEntry        :ListenerProxyEntry                              ;

            while(++i !== intLength)
            {
                objCurrentEntry = this.dictObjects[objToSearchIn][i];
                
                if(    objCurrentEntry.type        === strListenerType
                &&     objCurrentEntry.callback    === callback        )
                {
                    return i;    //if the specified listener is found, return the index
                }

                if(    objCurrentEntry.type        === strListenerType
                &&     callback                    === null            )
                {
                    ++intCurrentTypeCount;    //count the number of registred listeners for this type in the case that callback is not defined
                }
            }

            //used if callback is not defined within the params
            if(intCurrentTypeCount === 1)    //we've counted the number of registred listeners for the searched listenertype
            {
                return 0;    //we've only one registred listeners for this type, so return index 0
            }

            return -1;    //if nothing is found, return -1
        }


// -- GETTERS AND SETTERS -- //
        /**
         * Returns the number of the registered Objects
         * 
         * @return Number of registered Objects
         */
        public function get length():int
        {
            return this.intNumObjects;
        }
    }
}

//the following class is used for Singleton-Pattern, so only one instance can exist
internal class InternalListenerProxy {}

internal class ListenerProxyEntry
{
    private var funcCallback    :Function    ,
                strType            :String        ;
    
    public function ListenerProxyEntry(strType:String, funcCallback:Function):void
    {
        this.funcCallback    = funcCallback    ;
        this.strType        = strType        ;
    }
    
    
// -- PUBLIC METHODS -- //
    public function destruct():void
    {
        this.funcCallback    = null;
        this.strType        = null;
    }
    
    
// -- GETTERS AND SETTERS -- //
    public function get callback():Function
    {
        return this.funcCallback;
    }
    
    public function set callback(funcCallback:Function):void
    {
        this.funcCallback = funcCallback;
    }
    
    
    public function get type():String
    {
        return this.strType;
    }
    
    public function set type(strValue:String):void
    {
        this.strType = strValue;
    }
}