Events manager AS3 classes

2 Flares Twitter 2 Facebook 0 Google+ 0 Pin It Share 0 Email -- Filament.io 2 Flares ×

Basic Events manager classes. Remove all events, group events, suspend and resume events…

Features

  • Keep track of all events added and removed with this manager.
  • Add listener.
  • Remove listener.
  • Add a listener to one or more groups.
  • Suspsend a listener and/or a group of listeners without removing them.
  • Resume a listener and/or a group of listeners.
  • Remove all listeners from a group.
  • Remove all listeners.

For what purpose ?

Well, frankly, those events are sometimes a pain in the arse to manage.
Let’s say we have a bunch of buttons we just want to disable for some period of time and then resume them after.
Or imagine a full flash website that contains different sections. All those sections are very complex and contain subsections which themselves contain enough event listeners each to make you feel like you don’t even want to start to code them.
Well, this class just lets you suspend all events associated to a section or subsection (group) and resume them whenever you need them in just one line of code.

Here is a demo app just for you to test.
There are 3 sets of buttons, everyone of them have the MOUSE_OUT et MOUSE_OVER events registered.
They are subdivised in three main groups: group 1, group 2 and group 3.
And each event type is associated to a group: over for MOUSE_OVER event and out for MOUSE_OUT event.
That means that you can suspend and resume all buttons associated to a group in just one line of code (listeners can be associated to more than one group)

Get Adobe Flash player

How to use this class

EventsManager.as is singleton. That means that you can only have one single instance in your whole project.
To use it, just get its instance:

[sourcecode language='as3']
private var eManager:EventsManager = EventsManager.getInstance();
[/sourcecode]

Below are a couple of lines of code to help you use this class.

To add an event listener, use the add function like this:

[sourcecode language='as3']
eManager.add(eventDispatcher, MouseEvent.MOUSE_OVER, eventDispatcher_overHandler, ["group_name", "other_group_name"]);
[/sourcecode]

“group_name” and “other_group_name” are groups that you want the “eventDispatcher” to be associated to.

To remove all events from a group (or many groups, see how to suspend):

[sourcecode language='as3']
eManager.removeAllFromGroup(“group_name”);
[/sourcecode]

To suspend all events from many groups:

[sourcecode language='as3']
eManager.suspendAllFromGroup(“group_name”, “other_group_name”);
[/sourcecode]

Those functions are not the only one.

If you look at the functions description, you’ll see that some behaviors can be controlled. For example, you can add an event listener without letting this class register the event. Same when you remove the event. This can be usefull in some situations.

Classes are below, you can download the zip archive at then end of the post.

EventsManager.as

[sourcecode language='as3']
package rabbit.managers.events
{
	import flash.display.DisplayObject;
	import flash.events.EventDispatcher;
	import flash.utils.Dictionary;

	/**
	 * Singleton class
	 * @author Thomas John (thomas.john@open-design.be)
	 */
	public class EventsManager
	{
		// our unique instance of this class
		private static var instance:EventsManager = new EventsManager();

		// all of our disptachers
		private var dispatchers:Dictionary = new Dictionary(true);

		// groups of event dispatchers
		private var groups:Object =  { };

		/**
		 * Constructor : check if an instance already exists and if it does throw an error
		 */
		public function EventsManager()
		{
			if ( instance ) throw new Error( "EventsManager can only be accessed through EventsManager.getInstance()" );
			// init

		}

		/**
		 * Get unique instance of this singleton class
		 * @return					 Instance of this class
		 */
		public static function getInstance():EventsManager{
			return instance;
		}

		/**
		 * Add an event listener to the list
		 * @param	eventDispatcher
		 * @param	eventType
		 * @param	eventListener
		 * @param	eventGroup
		 * @param	autoAdd
		 * @param	eventUseCapture
		 */
		public function add(eventDispatcher:EventDispatcher, eventType:String, eventListener:Function, eventGroups:Array, autoAdd:Boolean = true, eventUseCapture:Boolean = false):void
		{
			var o:Object = dispatchers[eventDispatcher];

			// data for this dispatcher in our dictionary ?
			if ( o == null )
			{
				// no create an object to store it
				o = dispatchers[eventDispatcher] =  { };
			}

			// get array of nodes for specified type
			var a:Array = o[eventType] as Array;

			// array ?
			if ( a == null )
			{
				// no create it
				a = o[eventType] = [];
			}

			// check for duplicates
			var i:int = 0;
			var n:int = a.length;
			var node:DispatcherNode;

			for (i = 0; i < n; i++)
			{
				node = a[i] as DispatcherNode;

				if ( node.listener == eventListener &amp;amp;amp;amp;amp;amp;amp;&amp;amp;amp;amp;amp;amp;amp; node.useCapture == eventUseCapture)
				{
					// duplicate
					if ( autoAdd )
					{
						// restart event if needed
						node.startEvent();

						// and add it to new groups
						node.addToGroups(eventGroups);
					}

					return;
				}
			}

			// create node
			node = new DispatcherNode(eventDispatcher, eventType, eventListener, eventGroups, autoAdd, eventUseCapture);
			a.push(node);
		}

		/**
		 * Remove an event listener from the list
		 * @param	eventDispatcher
		 * @param	eventType
		 * @param	eventListener
		 * @param	autoRemove Auto remove listener.
		 * @param	eventUseCapture
		 * @param	autoRemoveFromAllGroup Auto remove node from all groups it was associated to.
		 * @param	groupsToRemoveFrom Remove node only from groups in this array.
		 */
		public function remove(eventDispatcher:EventDispatcher, eventType:String, eventListener:Function, autoRemove:Boolean = true, eventUseCapture:Boolean = false, autoRemoveFromAllGroup:Boolean = true, groupsToRemoveFrom:Array = null):void
		{
			// something ?
			if (dispatchers[eventDispatcher] == null || dispatchers[eventDispatcher][eventType] == null)
			{
				// no, quit function here
				return;
			}

			// get node
			var node:DispatcherNode = getNode(eventDispatcher, eventType, eventListener, eventUseCapture);

			removeNode(node, autoRemove, autoRemoveFromAllGroup, groupsToRemoveFrom);
		}

		/**
		 * Remove a node listener and remove this node from associated groups.
		 * @param	node
		 * @param	autoRemove
		 * @param	autoRemoveFromAllGroup
		 * @param	groupsToRemoveFrom
		 */
		public function removeNode(node:DispatcherNode, autoRemove:Boolean = true, autoRemoveFromAllGroup:Boolean = true, groupsToRemoveFrom:Array = null):void
		{
			if ( node != null )
			{
				if ( autoRemove )
				{
					node.stopEvent();
				}

				node.removeFromGroups(autoRemoveFromAllGroup, groupsToRemoveFrom);
			}
		}

		/**
		 * Remove all listeners from specified group.
		 * @param	group Group Name.
		 * @param	autoRemove Remove listener automatically.
		 */
		public function removeAllFromGroup(group:String, autoRemove:Boolean = true):void
		{
			if ( group == "" ) return;

			var a:Array = groups[group];

			// something ?
			if ( a == null) return;

			// go through all nodes in this array
			var node:DispatcherNode;

			while ( a.length > 0 )
			{
				node = a.pop() as DispatcherNode;
				removeNode(node, autoRemove);
			}
		}

		/**
		 * Remove all listeners and remove them from their associated groups.
		 * @param	autoRemove
		 */
		public function removeAll(autoRemove:Boolean = true):void
		{
			for each (var o:Object in dispatchers)
			{
				for each (var a:Array in o)
				{
					while ( a.length > 0 )
					{
						var node:DispatcherNode = a.pop() as DispatcherNode;

						if ( node != null )
						{
							if ( autoRemove )
							{
								node.stopEvent();
							}

							node.removeFromGroups(true);
						}
					}
				}
				//delete dispatchers[o];
			}
		}

		/**
		 * Suspend a listener. Can be resumed after.
		 * @param	eventDispatcher
		 * @param	eventType
		 * @param	eventListener
		 * @param	eventUseCapture
		 */
		public function suspend(eventDispatcher:EventDispatcher, eventType:String, eventListener:Function, eventUseCapture:Boolean = false):void
		{
			// something ?
			if (dispatchers[eventDispatcher] == null || dispatchers[eventDispatcher][eventType] == null)
			{
				// no, quit function here
				return;
			}

			// get node
			var node:DispatcherNode = getNode(eventDispatcher, eventType, eventListener, eventUseCapture);

			if ( node != null )
			{
				node.stopEvent();
			}
		}

		/**
		 * Suspend all listeners from a group.
		 * @param	group
		 */
		public function suspendAllFromGroup(group:String):void
		{
			if ( group == "" ) return;

			var a:Array = groups[group];

			// something ?
			if ( a == null) return;

			// go through all the nodes
			var i:int = 0;
			var n:int = a.length;
			var node:DispatcherNode;

			for (i = 0; i < n; i++)
			{
				node = a[i] as DispatcherNode;
				node.stopEvent();
			}
		}

		/**
		 * Resume a listener.
		 * @param	eventDispatcher
		 * @param	eventType
		 * @param	eventListener
		 * @param	eventUseCapture
		 */
		public function resume(eventDispatcher:EventDispatcher, eventType:String, eventListener:Function, eventUseCapture:Boolean = false):void
		{
			// something ?
			if (dispatchers[eventDispatcher] == null || dispatchers[eventDispatcher][eventType] == null)
			{
				// no, quit function here
				return;
			}

			// get node
			var node:DispatcherNode = getNode(eventDispatcher, eventType, eventListener, eventUseCapture);

			if ( node != null )
			{
				node.startEvent();
			}
		}

		/**
		 * Resume all listener from a group.
		 * @param	group
		 */
		public function resumeAllFromGroup(group:String):void
		{
			if ( group == "" ) return;

			var a:Array = groups[group];

			// something ?
			if ( a == null) return;

			// go through all the nodes
			var i:int = 0;
			var n:int = a.length;
			var node:DispatcherNode;

			for (i = 0; i < n; i++)
			{
				node = a[i] as DispatcherNode;
				node.startEvent();
			}
		}

		/**
		 * Add a node to a group.
		 * @param	node
		 * @param	group
		 */
		public function addNodeToGroup(node:DispatcherNode, group:String):void
		{
			// something ?
			if ( node == null || group == "" ) return;

			var pos:int = getNodePositionInGroup(node, group);

			if ( pos > -1 )
			{
				return;
			}

			// get group array
			var aGroup:Array = groups[group];

			if ( aGroup == null )
			{
				// no array, create it
				aGroup = groups[group] = [];
			}

			// add node to array
			aGroup.push(node);
		}

		/**
		 * Remove a node from a group.
		 * @param	node
		 * @param	group
		 */
		public function removeNodeFromGroup(node:DispatcherNode, group:String):void
		{
			// something ?
			if ( node == null || group == "" ) return;

			var pos:int = getNodePositionInGroup(node, group);

			if ( pos < 0 )
			{
				return;
			}

			// get group array
			var aGroup:Array = groups[group];

			if ( aGroup == null )
			{
				// group doesn't exist
				return;
			}

			// remove node from group
			aGroup.splice(pos, 1);
		}

		/**
		 * Get the node index in the specified group array.
		 * @param	node
		 * @param	group Group name (String).
		 * @return
		 */
		public function getNodePositionInGroup(node:DispatcherNode, group:String):int
		{
			// something ?
			if ( node == null || group == "" ) return -1;

			var a:Array = groups[group];

			// something ?
			if ( a == null) return -1;

			// look for it in the group array
			var i:int = 0;
			var n:int = a.length;
			var nodeInGroupList:DispatcherNode;

			for (i = 0; i < n; i++)
			{
				nodeInGroupList = a[i];

				if ( nodeInGroupList == node )
				{
					return i;
				}
			}

			return -1;
		}

		/**
		 * Get node.
		 * @param	eventDispatcher
		 * @param	eventType
		 * @param	eventListener
		 * @param	eventUseCapture
		 * @return
		 */
		public function getNode(eventDispatcher:EventDispatcher, eventType:String, eventListener:Function, eventUseCapture:Boolean = false):DispatcherNode
		{
			// something ?
			if (dispatchers[eventDispatcher] == null || dispatchers[eventDispatcher][eventType] == null)
			{
				// no, quit function here
				return null;
			}

			// get node
			// get node
			var a:Array = dispatchers[eventDispatcher][eventType] as Array;
			var i:int = 0;
			var n:int = a.length;
			var node:DispatcherNode;

			for (i = 0; i < n; i++)
			{
				node = a[i] as DispatcherNode;

				if ( node.listener == eventListener &amp;amp;amp;amp;amp;amp;amp;&amp;amp;amp;amp;amp;amp;amp; node.useCapture == eventUseCapture )
				{
					return node;
				}
			}

			return null;
		}
	}

}
[/sourcecode]

DispatcherNode.as

[sourcecode language='as3']
package rabbit.managers.events
{
	import flash.display.DisplayObject;
	import flash.events.EventDispatcher;

	/**
	 * ...
	 * @author Thomas John (thomas.john@open-design.be)
	 */
	public class DispatcherNode
	{
		private var eManager:EventsManager = EventsManager.getInstance();

		public var listener:Function = null;
		public var useCapture:Boolean = false;
		public var groups:Array = new Array();
		public var dispatcher:EventDispatcher = null;
		public var type:String = "";

		public function DispatcherNode(eventDispatcher:EventDispatcher, eventType:String, eventListener:Function, eventGroups:Array, autoAdd:Boolean = true, eventUseCapture:Boolean = false )
		{
			dispatcher = eventDispatcher;
			type = eventType;
			listener = eventListener;
			groups = eventGroups;
			useCapture = eventUseCapture;

			addToGroups(groups);

			if ( autoAdd ) startEvent();
		}

		/**
		 * Start listening for event
		 */
		public function startEvent():void
		{
			dispatcher.addEventListener(type, listener, useCapture);
		}

		/**
		 * Stop listening for event
		 */
		public function stopEvent():void
		{
			dispatcher.removeEventListener(type, listener, useCapture);
		}

		/**
		 * Add this node to specified groups
		 * @param	eventGroups
		 */
		public function addToGroups(eventGroups:Array):void
		{
			var i:int = 0;
			var n:int = eventGroups.length;

			for (i = 0; i < n; i++)
			{
				eManager.addNodeToGroup(this, eventGroups[i]);
			}
		}

		/**
		 * Remove node from specified groups.
		 * @param	removeFromAllGroups If set to true then this node will be removed from all its associated groups.
		 * @param	groupsToRemoveFrom If removeFromAllGroups is set to false then this node will be removed from specified groups in this array.
		 */
		public function removeFromGroups(removeFromAllGroups:Boolean = true, groupsToRemoveFrom:Array = null):void
		{
			var i:int = 0;
			var n:int = 0;

			if ( removeFromAllGroups )
			{
				n = groups.length;

				for (i = 0; i < n; i++)
				{
					eManager.removeNodeFromGroup(this, groups[i]);
				}
			}
			else if( groupsToRemoveFrom != null )
			{
				n = groupsToRemoveFrom.length;

				for (i = 0; i < n; i++)
				{
					eManager.removeNodeFromGroup(this, groupsToRemoveFrom[i]);
				}
			}
		}
	}

}
[/sourcecode]

Download source code in ActionScript3: Events manager classes in AS3

2 Flares Twitter 2 Facebook 0 Google+ 0 Pin It Share 0 Email -- Filament.io 2 Flares ×

13 Comments

  1. Michael

    Some time ago, I realized that those Listeners can crash your app, if you loose track of them and add \’em over and over again.
    Right now I\’m not so sure if Flash Player 10 has an improved Garbage Collector for eventlisteners to display objects which actually are not on the stage.

    however – it\’s always nice to write one line of code instead of many …

    http://code.google.com/p/as3listenermanager/
    (self instanciating singleton with the first add method)

    greetings
    Michael

  2. admin

    Hi Michael,
    looks like your manager has the same functionalities than mine but I’m wondering why the use of objects to pass as parameters to your functions ? Wouldn’t that be easier to use actual function parameters instead and put optional ones at the end ?
    And what’s the use of the “mod” value ? in your wiki, you put an example with an “alpha” and “width” value passed to that “mod” value. What’s the purpose of it ?

    Anyway, thanks for passing by.

    Cheers.

  3. Michael

    I’m looking for new advanced flexible ways how to add() with less casting (EventDispatcher as Object as Array as ..) the “… list” operator is a really good one, but is lacking the typechecking, the only downside – you have to do it for yourself manually.

    Manager.add({instance: button,
    events: [CLICK, OVER, OUT],
    mapTo: [clickHandler, overHandler, outHandler],
    info: „page1“});

    would save some more lines

    if you want to disable/suspend listeners of a button, you maybe want to disable the button visually, too – since your manager knows all the buttons for group1 already, why don’t let him do it (instead of btn1.alpha = 0.25; btn2.alpha = 0.25; …. btn8.alpha = 0.25;)

    however I appreciate your work

    cheers.

  4. yeah but then you’re gonna write a class that’s going to handle too many things.
    I prefer too keep things separate. Events with events, display objects with display objects, …
    But yeah, I understand your point of view, in some simple websites, this can be useful.

  5. Michael

    What about sound, security, error, netstatus events? If you have an abstract manager to handle all kind of events, not only display objects events, maybe you want a way back from the manager for the suspending/resuming/removing part – what do you think?

    so here is maybe a good idea to pass an optional “function” as a parameter for the suspend/resume/remove method. this is what a manager does right? linking stuff together and call the right methods in each situation?

  6. Mhhhhh…I’m not following you there… sorry I guess my english is not that good… this event manager does handle all kind of events. I just believe that some things are better when kept separate. It’s not for one more line of code. I mean, you will always be able to write a class that manages everything in just one line of code, but then what ?
    Anyway, I’m probably not understanding you right.

  7. No, don’t think so. Or maybe… you know, with all those theoretical stuff one might just be using it without even noticing it.
    Anyway, it all depends on the projects one is working on and I don’t think that kind of thing suits me for the kind of project I’m working on… ;)

  8. Davisete

    Hello Im a newbie in actionscript i need to open the fla because i can put to view send to functions buttons to class. You can upload the fla ?
    Thanks for listen to me.

  9. Hi,

    there is no .fla, the sample program is actually made from pure code with the help of FlashDevelop (see links on the right).
    Sorry if I don’t share the code of this sample movie, I just don’t feel like doing so. Hope you understand. But all you need to understand how to use this events manager class is on this page. Good luck.

  10. Thanx for the code Thomas John, went in easy and works a treat :)

  11. vi

    thanks for the classes. i don’t see an immediate use in my projects, but i’m sure i’ll be using it soon.

Trackbacks for this post

  1. AS3 Game Engine – Part 2 – Waypoints & Event Hub

Leave a Comment