Commits

Eugene Veretennikov committed 446573b

robotlegs 1.5.2
swiftsuspenders 1.6.0

Comments (0)

Files changed (84)

+syntax: glob
+*/target/*

as3signals/pom.xml

     </parent>
 
     <artifactId>as3signals</artifactId>
-    <version>0.0.1-SNAPSHOT</version>
+    <version>0.9-BETA</version>
     <packaging>swc</packaging>
     <name>as3signals</name>
     <url>https://github.com/robertpenner/as3-signals</url>

as3signals/src/org/osflash/signals/DeluxeSignal.as

 package org.osflash.signals
 {
-	import flash.utils.Dictionary;
-
 	import org.osflash.signals.events.IBubbleEventHandler;
 	import org.osflash.signals.events.IEvent;
 
 	 * Allows the valueClasses to be set in MXML, e.g.
 	 * <signals:Signal id="nameChanged">{[String, uint]}</signals:Signal>
 	 */
-	[DefaultProperty("valueClasses")]	
-	
+	[DefaultProperty("valueClasses")]
 	/**
 	 * Signal dispatches events to multiple listeners.
 	 * It is inspired by C# events and delegates, and by
 	 * <br/><br/>
 	 * Project home: <a target="_top" href="http://github.com/robertpenner/as3-signals/">http://github.com/robertpenner/as3-signals/</a>
 	 */
-	public class DeluxeSignal extends Signal implements IPrioritySignal
+	public class DeluxeSignal extends PrioritySignal
 	{
 		protected var _target:Object;
-		
+
 		/**
 		 * Creates a DeluxeSignal instance to dispatch events on behalf of a target object.
 		 * @param	target The object the signal is dispatching events on behalf of.
 		public function DeluxeSignal(target:Object = null, ...valueClasses)
 		{
 			_target = target;
+			// Cannot use super.apply(null, valueClasses), so allow the subclass to call super(valueClasses).
+			valueClasses = (valueClasses.length == 1 && valueClasses[0] is Array) ? valueClasses[0]:valueClasses;
+			
 			super(valueClasses);
 		}
-		
+
 		/** @inheritDoc */
-		public function get target():Object { return _target; }
-		
+		public function get target():Object	{ return _target; }
+
 		public function set target(value:Object):void
 		{
 			if (value == _target) return;
 			removeAll();
 			_target = value;
 		}
-		
-		//TODO: @throws
-		override public function add(listener:Function):Function
-		{
-			return addWithPriority(listener);
-		}
-		
-		public function addWithPriority(listener:Function, priority:int = 0):Function
-		{
-			registerListenerWithPriority(listener, false, priority);
-			return listener;
-		}
-		
-		override public function addOnce(listener:Function):Function
-		{
-			return addOnceWithPriority(listener);
-		}
-		
-		/** @inheritDoc */
-		public function addOnceWithPriority(listener:Function, priority:int = 0):Function
-		{
-			registerListenerWithPriority(listener, true, priority);
-			return listener;
-		}
-		
-		/** @inheritDoc */
+
+		/**
+		 * @inheritDoc
+		 * @throws ArgumentError <code>ArgumentError</code>: Incorrect number of arguments.
+		 * @throws ArgumentError <code>ArgumentError</code>: Value object is not an instance of the appropriate valueClasses Class.
+		 */
 		override public function dispatch(...valueObjects):void
 		{
-			//
 			// Validate value objects against pre-defined value classes.
-			//
-
-			var valueObject:Object;
-			var valueClass:Class;
-
 			const numValueClasses:int = _valueClasses.length;
 			const numValueObjects:int = valueObjects.length;
+			
+			if (numValueObjects < numValueClasses)
+			{
+				throw new ArgumentError('Incorrect number of arguments. '+
+					'Expected at least '+numValueClasses+' but received '+
+					numValueObjects+'.');
+			}
 
+			// Cannot dispatch differently typed objects than declared classes.
 			for (var i:int = 0; i < numValueClasses; i++)
 			{
-				valueObject = valueObjects[i];
-				valueClass = _valueClasses[i];
-
-				if (valueObject === null || valueObject is valueClass) continue;
+				// Optimized for the optimistic case that values are correct.
+				if (valueObjects[i] is _valueClasses[i] || valueObjects[i] === null) 
+					continue;
 					
-				throw new ArgumentError('Value object <'+valueObject
-					+'> is not an instance of <'+valueClass+'>.');
+				throw new ArgumentError('Value object <'+valueObjects[i]
+					+'> is not an instance of <'+_valueClasses[i]+'>.');
 			}
 
-			//
 			// Extract and clone event object if necessary.
-			//
-
 			var event:IEvent = valueObjects[0] as IEvent;
-
 			if (event)
 			{
 				if (event.target)
 				event.signal = this;
 			}
 
-			//
 			// Broadcast to listeners.
-			//
-
-			var bindingsToProcess: SignalBindingList = bindings;
-			
-			if (bindingsToProcess.nonEmpty)
+			var slotsToProcess:SlotList = slots;
+			while (slotsToProcess.nonEmpty)
 			{
-				if (numValueObjects == 0)
-				{
-					while (bindingsToProcess.nonEmpty)
-					{
-						bindingsToProcess.head.execute0();
-						bindingsToProcess = bindingsToProcess.tail;
-					}
-				}
-				else if (numValueObjects == 1)
-				{
-					const singleValue:Object = valueObjects[0];
-
-					while (bindingsToProcess.nonEmpty)
-					{
-						bindingsToProcess.head.execute1(singleValue);
-						bindingsToProcess = bindingsToProcess.tail;
-					}
-				}
-				else if (numValueObjects == 2)
-				{
-					const value1:Object = valueObjects[0];
-					const value2:Object = valueObjects[1];
-
-					while (bindingsToProcess.nonEmpty)
-					{
-						bindingsToProcess.head.execute2(value1, value2);
-						bindingsToProcess = bindingsToProcess.tail;
-					}
-				}
-				else
-				{
-					while (bindingsToProcess.nonEmpty)
-					{
-						bindingsToProcess.head.execute(valueObjects);
-						bindingsToProcess = bindingsToProcess.tail;
-					}
-				}
+				slotsToProcess.head.execute(valueObjects);
+				slotsToProcess = slotsToProcess.tail;
 			}
 
-			//
 			// Bubble the event as far as possible.
-			//
-			
 			if (!event || !event.bubbles) return;
 
 			var currentTarget:Object = target;
 
-			while (currentTarget && currentTarget.hasOwnProperty("parent") && (currentTarget = currentTarget.parent))
+			while (currentTarget && currentTarget.hasOwnProperty("parent") 
+				   && (currentTarget = currentTarget["parent"]))
 			{
 				if (currentTarget is IBubbleEventHandler)
 				{
-					//
 					// onEventBubbled() can stop the bubbling by returning false.
-					//
-					
 					if (!IBubbleEventHandler(event.currentTarget = currentTarget).onEventBubbled(event))
 						break;
 				}
 			}
 		}
-		
-		override protected function registerListener(listener:Function, once:Boolean = false):void
-		{
-			registerListenerWithPriority(listener, once);
-		}
-		
-		protected function registerListenerWithPriority(listener:Function, once:Boolean = false, priority:int = 0):void
-		{
-			if (!bindings.nonEmpty || verifyRegistrationOf(listener, once))
-			{
-				bindings = bindings.insertWithPriority(new SignalBinding(listener, once, this, priority));
 
-				if (null == existing) existing = new Dictionary();
-
-				existing[listener] = true;
-			}
-		}
 	}
 }

as3signals/src/org/osflash/signals/IOnceSignal.as

+package org.osflash.signals
+{
+	/**
+	 *
+	 */
+	public interface IOnceSignal
+	{
+		/**
+		 * An optional array of classes defining the types of parameters sent to listeners.
+		 */
+		function get valueClasses():Array;
+		function set valueClasses(value:Array):void;
+		
+		/** The current number of listeners for the signal. */
+		function get numListeners():uint;
+		
+		/**
+		 * Subscribes a one-time listener for this signal.
+		 * The signal will remove the listener automatically the first time it is called,
+		 * after the dispatch to all listeners is complete.
+		 * @param	listener A function with arguments
+		 * that matches the value classes dispatched by the signal.
+		 * If value classes are not specified (e.g. via Signal constructor), dispatch() can be called without arguments.
+		 * @return a ISlot, which contains the Function passed as the parameter
+		 */
+		function addOnce(listener:Function):ISlot;
+
+		/**
+		 * Dispatches an object to listeners.
+		 * @param	valueObjects	Any number of parameters to send to listeners. Will be type-checked against valueClasses.
+		 * @throws	ArgumentError	<code>ArgumentError</code>:	valueObjects are not compatible with valueClasses.
+		 */
+		function dispatch(...valueObjects):void;
+		
+		/**
+		 * Unsubscribes a listener from the signal.
+		 * @param	listener
+		 * @return a ISlot, which contains the Function passed as the parameter
+		 */
+		function remove(listener:Function):ISlot;
+
+		/**
+		 * Unsubscribes all listeners from the signal.
+		 */
+		function removeAll():void
+	}
+}

as3signals/src/org/osflash/signals/IPrioritySignal.as

 		 * @param	listener A function with an argument
 		 * that matches the type of event dispatched by the signal.
 		 * If eventClass is not specified, the listener and dispatch() can be called without an argument.
+		 * @return a ISlot, which contains the Function passed as the parameter
+		 * @see ISlot
 		 */
-		function addWithPriority(listener:Function, priority:int = 0):Function
+		function addWithPriority(listener:Function, priority:int = 0):ISlot
 		
 		/**
 		 * Subscribes a one-time listener for this signal.
 		 * The priority is designated by a signed 32-bit integer.
 		 * The higher the number, the higher the priority.
 		 * All listeners with priority n are processed before listeners of priority n-1.
+		 * @return a ISlot, which contains the Function passed as the parameter
+		 * @see ISlot
 		 */
-		function addOnceWithPriority(listener:Function, priority:int = 0):Function
+		function addOnceWithPriority(listener:Function, priority:int = 0):ISlot
 	}
 }

as3signals/src/org/osflash/signals/ISignal.as

 	/**
 	 *
 	 */
-	public interface ISignal
+	public interface ISignal extends IOnceSignal
 	{
 		/**
-		 * An optional array of classes defining the types of parameters sent to listeners.
-		 */
-		function get valueClasses():Array;
-		function set valueClasses(value:Array):void;
-		
-		/** The current number of listeners for the signal. */
-		function get numListeners():uint;
-		
-		/**
 		 * Subscribes a listener for the signal.
 		 * @param	listener A function with arguments
 		 * that matches the value classes dispatched by the signal.
 		 * If value classes are not specified (e.g. via Signal constructor), dispatch() can be called without arguments.
-		 * @return the listener Function passed as the parameter
+		 * @return a ISlot, which contains the Function passed as the parameter
 		 */
-		function add(listener:Function):Function;
-		
-		/**
-		 * Subscribes a one-time listener for this signal.
-		 * The signal will remove the listener automatically the first time it is called,
-		 * after the dispatch to all listeners is complete.
-		 * @param	listener A function with arguments
-		 * that matches the value classes dispatched by the signal.
-		 * If value classes are not specified (e.g. via Signal constructor), dispatch() can be called without arguments.
-		 * @return the listener Function passed as the parameter
-		 */
-		function addOnce(listener:Function):Function;
-
-		/**
-		 * Dispatches an object to listeners.
-		 * @param	valueObjects	Any number of parameters to send to listeners. Will be type-checked against valueClasses.
-		 * @throws	ArgumentError	<code>ArgumentError</code>:	valueObjects are not compatible with valueClasses.
-		 */
-		function dispatch(...valueObjects):void;
-		
-		/**
-		 * Unsubscribes a listener from the signal.
-		 * @param	listener
-		 * @return the listener Function passed as the parameter
-		 */
-		function remove(listener:Function):Function;
-
-		/**
-		 * Unsubscribes all listeners from the signal.
-		 */
-		function removeAll():void
+		function add(listener:Function):ISlot;
 	}
 }

as3signals/src/org/osflash/signals/ISignalBinding.as

-/**
- * Created by ${PRODUCT_NAME}.
- * User: joa
- * Date: 21.11.10
- * Time: 21:19
- * To change this template use File | Settings | File Templates.
- */
-package org.osflash.signals
-{
-	/**
-	 * The ISignalBinding interface defines the basic properties of a
-	 * listener associated with a Signal.
-	 *
-	 * @author Joa Ebert
-	 */
-	public interface ISignalBinding
-	{
-		/**
-		 * The listener associated with this binding.
-		 */
-		function get listener():Function;
-		function set listener(value:Function):void;
-
-		/**
-		 * Whether or not this binding is destroyed after it has been used once.
-		 */
-		function get once():Boolean;
-
-		/**
-		 * The priority of this binding.
-		 */
-		function get priority(): int;
-
-		/**
-		 * Pauses this binding.
-		 */
-		function pause(): void;
-
-		/**
-		 * Resumes this binding if it has been paused before.
-		 */
-		function resume(): void;
-
-		/**
-		 * Executes a listener of arity <code>n</code> where <code>n</code> is
-		 * <code>valueObjects.length</code>.
-		 *
-		 * @param valueObjects The array of arguments to be applied to the listener.
-		 */
-		function execute(valueObjects:Array):void;
-
-		/**
-		 * Executes a listener of arity <code>0</code>.
-		 */
-		function execute0():void;
-
-		/**
-		 * Executes a listener of arity <code>1</code>.
-		 *
-		 * @param value1 The argument for the listener.
-		 */
-		function execute1(value1:Object):void;
-
-		/**
-		 * Executes a listener of arity <code>2</code>.
-		 *
-		 * @param value1 The first argument for the listener.
-		 * @param value2 The second argument for the listener.
-		 */
-		function execute2(value1:Object, value2:Object):void;
-
-		/**
-		 * Removes the binding from its signal.
-		 */
-		function remove(): void;
-	}
-}

as3signals/src/org/osflash/signals/ISlot.as

+package org.osflash.signals
+{
+	/**
+	 * The ISlot interface defines the basic properties of a
+	 * listener associated with a Signal.
+	 *
+	 * @author Joa Ebert
+	 * @author Robert Penner
+	 */
+	public interface ISlot
+	{
+		/**
+		 * The listener associated with this slot.
+		 */
+		function get listener():Function;
+		function set listener(value:Function):void;
+		
+		/**
+		 * Allows the ISlot to inject parameters when dispatching. The params will be at 
+		 * the tail of the arguments and the ISignal arguments will be at the head.
+		 * 
+		 * var signal:ISignal = new Signal(String);
+		 * signal.add(handler).params = [42];
+		 * signal.dispatch('The Answer');
+		 * function handler(name:String, num:int):void{}
+		 */
+		function get params():Array;
+		function set params(value:Array):void;
+
+		/**
+		 * Whether this slot is automatically removed after it has been used once.
+		 */
+		function get once():Boolean;
+
+		/**
+		 * The priority of this slot should be given in the execution order.
+		 * An IPrioritySignal will call higher numbers before lower ones.
+		 * Defaults to 0.
+		 */
+		function get priority():int;
+		
+		/**
+		 * Whether the listener is called on execution. Defaults to true.
+		 */
+		function get enabled():Boolean;
+		function set enabled(value:Boolean):void;
+		
+		/**
+		 * Executes a listener without arguments.
+		 * Existing <code>params</code> are appended before the listener is called.
+		 */
+		function execute0():void;		
+
+		/**
+		 * Dispatches one argument to a listener.
+		 * Existing <code>params</code> are appended before the listener is called.
+		 * @param value The argument for the listener.
+		 */
+		function execute1(value:Object):void;		
+
+		/**
+		 * Executes a listener of arity <code>n</code> where <code>n</code> is
+		 * <code>valueObjects.length</code>.
+		 * Existing <code>params</code> are appended before the listener is called.
+		 * @param valueObjects The array of arguments to be applied to the listener.
+		 */
+		function execute(valueObjects:Array):void;
+		
+		/**
+		 * Removes the slot from its signal.
+		 */
+		function remove():void;
+	}
+}

as3signals/src/org/osflash/signals/MonoSignal.as

+package org.osflash.signals
+{
+	import flash.errors.IllegalOperationError;
+	import flash.utils.getQualifiedClassName;
+	
+	/** 
+	 * Allows the valueClasses to be set in MXML, e.g.
+	 * <signals:Signal id="nameChanged">{[String, uint]}</signals:Signal>
+	 */
+	[DefaultProperty("valueClasses")]	
+	
+	/**
+	 * A MonoSignal can have only one listener.
+	 */
+	public class MonoSignal implements ISignal
+	{
+		protected var _valueClasses:Array;		// of Class
+		
+		protected var slot:Slot;
+		
+		/**
+		 * Creates a MonoSignal instance to dispatch value objects.
+		 * @param	valueClasses Any number of class references that enable type checks in dispatch().
+		 * For example, new Signal(String, uint)
+		 * would allow: signal.dispatch("the Answer", 42)
+		 * but not: signal.dispatch(true, 42.5)
+		 * nor: signal.dispatch()
+		 *
+		 * NOTE: Subclasses cannot call super.apply(null, valueClasses),
+		 * but this constructor has logic to support super(valueClasses).
+		 */
+		public function MonoSignal(...valueClasses)
+		{
+			// Cannot use super.apply(null, valueClasses), so allow the subclass to call super(valueClasses).
+			this.valueClasses = (valueClasses.length == 1 && valueClasses[0] is Array) ? valueClasses[0] : valueClasses;
+		}
+		
+		/**
+		 * @inheritDoc
+		 * @throws ArgumentError <code>ArgumentError</code>: Invalid valueClasses argument: item at index should be a Class but was not.
+		 */
+		[ArrayElementType("Class")]
+		public function get valueClasses():Array { return _valueClasses; }
+		
+		public function set valueClasses(value:Array):void
+		{
+			// Clone so the Array cannot be affected from outside.
+			_valueClasses = value ? value.slice() : [];
+			for (var i:int = _valueClasses.length; i--; )
+			{
+				if (!(_valueClasses[i] is Class))
+				{
+					throw new ArgumentError('Invalid valueClasses argument: ' +
+						'item at index ' + i + ' should be a Class but was:<' +
+						_valueClasses[i] + '>.' + getQualifiedClassName(_valueClasses[i]));
+				}
+			}
+		}
+		
+		/** @inheritDoc */
+		public function get numListeners():uint { return slot ? 1 : 0; }
+		
+		/**
+		 * @inheritDoc
+		 * @throws flash.errors.IllegalOperationError <code>IllegalOperationError</code>: You cannot add or addOnce with a listener already added, remove the current listener first.
+		 * @throws ArgumentError <code>ArgumentError</code>: Given listener is <code>null</code>.
+		 */
+		public function add(listener:Function):ISlot
+		{
+			return registerListener(listener);
+		}
+		
+		/**
+		 * @inheritDoc
+		 * @throws flash.errors.IllegalOperationError <code>IllegalOperationError</code>: You cannot add or addOnce with a listener already added, remove the current listener first.
+		 * @throws ArgumentError <code>ArgumentError</code>: Given listener is <code>null</code>.
+		 */
+		public function addOnce(listener:Function):ISlot
+		{
+			return registerListener(listener, true);
+		}
+		
+		/** @inheritDoc */
+		public function remove(listener:Function):ISlot
+		{
+			if (slot && slot.listener == listener)
+			{
+				const theSlot:ISlot = slot;
+				slot = null;
+				return theSlot;
+			}
+
+			return null;
+		}
+		
+		/** @inheritDoc */
+		public function removeAll():void
+		{
+			if (slot) slot.remove();
+		}
+		
+		/**
+		 * @inheritDoc
+		 * @throws ArgumentError <code>ArgumentError</code>: Incorrect number of arguments.
+		 * @throws ArgumentError <code>ArgumentError</code>: Value object is not an instance of the appropriate valueClasses Class.
+		 */
+		public function dispatch(...valueObjects):void
+		{
+			// If valueClasses is empty, value objects are not type-checked. 
+			const numValueClasses:int = _valueClasses.length;
+			const numValueObjects:int = valueObjects.length;
+
+			// Cannot dispatch fewer objects than declared classes.
+			if (numValueObjects < numValueClasses)
+			{
+				throw new ArgumentError('Incorrect number of arguments. '+
+					'Expected at least '+numValueClasses+' but received '+
+					numValueObjects+'.');
+			}
+			
+			// Cannot dispatch differently typed objects than declared classes.
+			for (var i:int = 0; i < numValueClasses; i++)
+			{
+				// Optimized for the optimistic case that values are correct.
+				if (valueObjects[i] is _valueClasses[i] || valueObjects[i] === null) 
+					continue;
+					
+				throw new ArgumentError('Value object <'+valueObjects[i]
+					+'> is not an instance of <'+_valueClasses[i]+'>.');
+			}
+
+			// Broadcast to the one listener.
+			if (slot)
+			{
+				slot.execute(valueObjects);
+			}
+		}
+		
+		protected function registerListener(listener:Function, once:Boolean = false):ISlot
+		{
+			if (slot) 
+			{
+				// If the listener exits previously added, definitely don't add it.
+				throw new IllegalOperationError('You cannot add or addOnce with a listener already added, remove the current listener first.');
+			}
+			
+			return (slot = new Slot(listener, this, once));
+		}
+
+	}
+}

as3signals/src/org/osflash/signals/OnceSignal.as

+package org.osflash.signals
+{
+	import flash.errors.IllegalOperationError;
+	import flash.utils.getQualifiedClassName;
+
+	/** 
+	 * Allows the valueClasses to be set in MXML, e.g.
+	 * <signals:Signal id="nameChanged">{[String, uint]}</signals:Signal>
+	 */
+	[DefaultProperty("valueClasses")]	
+	
+	/**
+	 * Signal dispatches events to multiple listeners.
+	 * It is inspired by C# events and delegates, and by
+	 * <a target="_top" href="http://en.wikipedia.org/wiki/Signals_and_slots">signals and slots</a>
+	 * in Qt.
+	 * A Signal adds event dispatching functionality through composition and interfaces,
+	 * rather than inheriting from a dispatcher.
+	 * <br/><br/>
+	 * Project home: <a target="_top" href="http://github.com/robertpenner/as3-signals/">http://github.com/robertpenner/as3-signals/</a>
+	 */
+	public class OnceSignal implements IOnceSignal
+	{
+		protected var _valueClasses:Array;		// of Class
+		protected var slots:SlotList = SlotList.NIL;
+		
+		/**
+		 * Creates a Signal instance to dispatch value objects.
+		 * @param	valueClasses Any number of class references that enable type checks in dispatch().
+		 * For example, new Signal(String, uint)
+		 * would allow: signal.dispatch("the Answer", 42)
+		 * but not: signal.dispatch(true, 42.5)
+		 * nor: signal.dispatch()
+		 *
+		 * NOTE: In AS3, subclasses cannot call super.apply(null, valueClasses),
+		 * but this constructor has logic to support super(valueClasses).
+		 */
+		public function OnceSignal(...valueClasses)
+		{
+			// Cannot use super.apply(null, valueClasses), so allow the subclass to call super(valueClasses).
+			this.valueClasses = (valueClasses.length == 1 && valueClasses[0] is Array) ? valueClasses[0] : valueClasses;
+		}
+		
+		/**
+		 * @inheritDoc
+		 * @throws ArgumentError <code>ArgumentError</code>: Invalid valueClasses argument: item at index should be a Class but was not.
+		 */
+		[ArrayElementType("Class")]
+		public function get valueClasses():Array { return _valueClasses; }
+		
+		public function set valueClasses(value:Array):void
+		{
+			// Clone so the Array cannot be affected from outside.
+			_valueClasses = value ? value.slice() : [];
+			for (var i:int = _valueClasses.length; i--; )
+			{
+				if (!(_valueClasses[i] is Class))
+				{
+					throw new ArgumentError('Invalid valueClasses argument: ' +
+						'item at index ' + i + ' should be a Class but was:<' +
+						_valueClasses[i] + '>.' + getQualifiedClassName(_valueClasses[i]));
+				}
+			}
+		}
+		
+		/** @inheritDoc */
+		public function get numListeners():uint { return slots.length; }
+		
+		/**
+		 * @inheritDoc
+		 * @throws flash.errors.IllegalOperationError <code>IllegalOperationError</code>: You cannot addOnce() then add() the same listener without removing the relationship first.
+		 * @throws ArgumentError <code>ArgumentError</code>: Given listener is <code>null</code>.
+		 */
+		public function addOnce(listener:Function):ISlot
+		{
+			return registerListener(listener, true);
+		}
+		
+		/** @inheritDoc */
+		public function remove(listener:Function):ISlot
+		{
+			const slot:ISlot = slots.find(listener);
+			if (!slot) return null;
+			
+			slots = slots.filterNot(listener);
+			return slot;
+		}
+		
+		/** @inheritDoc */
+		public function removeAll():void
+		{
+			slots = SlotList.NIL;
+		}
+		
+		/**
+		 * @inheritDoc
+		 * @throws ArgumentError <code>ArgumentError</code>: Incorrect number of arguments.
+		 * @throws ArgumentError <code>ArgumentError</code>: Value object is not an instance of the appropriate valueClasses Class.
+		 */
+		public function dispatch(...valueObjects):void
+		{
+			
+			// If valueClasses is empty, value objects are not type-checked. 
+			const numValueClasses:int = _valueClasses.length;
+			const numValueObjects:int = valueObjects.length;
+
+			// Cannot dispatch fewer objects than declared classes.
+			if (numValueObjects < numValueClasses)
+			{
+				throw new ArgumentError('Incorrect number of arguments. '+
+					'Expected at least '+numValueClasses+' but received '+
+					numValueObjects+'.');
+			}
+			
+			// Cannot dispatch differently typed objects than declared classes.
+			for (var i:int = 0; i < numValueClasses; i++)
+			{
+				// Optimized for the optimistic case that values are correct.
+				if (valueObjects[i] is _valueClasses[i] || valueObjects[i] === null) 
+					continue;
+					
+				throw new ArgumentError('Value object <'+valueObjects[i]
+					+'> is not an instance of <'+_valueClasses[i]+'>.');
+			}
+
+			// Broadcast to listeners.
+			var slotsToProcess:SlotList = slots;
+			if(slotsToProcess.nonEmpty)
+			{
+				while (slotsToProcess.nonEmpty)
+				{
+					slotsToProcess.head.execute(valueObjects);
+					slotsToProcess = slotsToProcess.tail;
+				}
+			}
+		}
+
+		protected function registerListener(listener:Function, once:Boolean = false):ISlot
+		{
+			if (registrationPossible(listener, once))
+			{
+				const newSlot:ISlot = new Slot(listener, this, once);
+				slots = slots.prepend(newSlot);
+				return newSlot;
+			}
+			
+			return slots.find(listener);
+		}
+
+		protected function registrationPossible(listener:Function, once:Boolean):Boolean
+		{
+			if (!slots.nonEmpty) return true;
+			
+			const existingSlot:ISlot = slots.find(listener);
+			if (!existingSlot) return true;
+
+			if (existingSlot.once != once)
+			{
+				// If the listener was previously added, definitely don't add it again.
+				// But throw an exception if their once values differ.
+				throw new IllegalOperationError('You cannot addOnce() then add() the same listener without removing the relationship first.');
+			}
+			
+			return false; // Listener was already registered.
+		}
+	}
+}

as3signals/src/org/osflash/signals/PrioritySignal.as

+package org.osflash.signals 
+{
+	public class PrioritySignal extends Signal implements IPrioritySignal 
+	{
+
+		public function PrioritySignal(...valueClasses) 
+		{
+			// Cannot use super.apply(null, valueClasses), so allow the subclass to call super(valueClasses).
+			valueClasses = (valueClasses.length == 1 && valueClasses[0] is Array) ? valueClasses[0] : valueClasses;
+
+			super(valueClasses);
+		}
+
+		/**
+		 * @inheritDoc
+		 * @throws flash.errors.IllegalOperationError <code>IllegalOperationError</code>: You cannot addOnce() then add() the same listener without removing the relationship first.
+		 * @throws ArgumentError <code>ArgumentError</code>: Given listener is <code>null</code>.
+		 */
+		public function addWithPriority(listener:Function, priority:int = 0):ISlot 
+		{
+			return registerListenerWithPriority(listener, false, priority);
+		}
+
+		/**
+		 * @inheritDoc
+		 * @throws flash.errors.IllegalOperationError <code>IllegalOperationError</code>: You cannot addOnce() then add() the same listener without removing the relationship first.
+		 * @throws ArgumentError <code>ArgumentError</code>: Given listener is <code>null</code>.
+		 */
+		public function addOnceWithPriority(listener:Function, priority:int = 0):ISlot 
+		{
+			return registerListenerWithPriority(listener, true, priority);
+		}
+
+		override protected function registerListener(listener:Function, once:Boolean = false):ISlot 
+		{
+			return registerListenerWithPriority(listener, once);
+		}
+
+		protected function registerListenerWithPriority(listener:Function, once:Boolean = false, priority:int = 0):ISlot
+		{
+			if (registrationPossible(listener, once))
+			{
+				const slot:ISlot = new Slot(listener, this, once, priority);
+				slots = slots.insertWithPriority(slot);
+				return slot;
+			}
+			
+			return slots.find(listener);
+		}
+
+	}
+}

as3signals/src/org/osflash/signals/Signal.as

 package org.osflash.signals
 {
-	import flash.errors.IllegalOperationError;
-	import flash.utils.Dictionary;
-	import flash.utils.getQualifiedClassName;
 
 	/** 
 	 * Allows the valueClasses to be set in MXML, e.g.
 	 * <br/><br/>
 	 * Project home: <a target="_top" href="http://github.com/robertpenner/as3-signals/">http://github.com/robertpenner/as3-signals/</a>
 	 */
-	public class Signal implements ISignal
+	public class Signal extends OnceSignal implements ISignal
 	{
-		protected var _valueClasses:Array;		// of Class
-
-		protected var bindings:SignalBindingList;
-		protected var existing:Dictionary;
-		
 		/**
 		 * Creates a Signal instance to dispatch value objects.
 		 * @param	valueClasses Any number of class references that enable type checks in dispatch().
 		 * but not: signal.dispatch(true, 42.5)
 		 * nor: signal.dispatch()
 		 *
-		 * NOTE: Subclasses cannot call super.apply(null, valueClasses),
+		 * NOTE: In AS3, subclasses cannot call super.apply(null, valueClasses),
 		 * but this constructor has logic to support super(valueClasses).
 		 */
 		public function Signal(...valueClasses)
 		{
-			bindings = SignalBindingList.NIL;
-			existing = null;
-
 			// Cannot use super.apply(null, valueClasses), so allow the subclass to call super(valueClasses).
-			this.valueClasses = (valueClasses.length == 1 && valueClasses[0] is Array) ? valueClasses[0] : valueClasses;
+			valueClasses = (valueClasses.length == 1 && valueClasses[0] is Array) ? valueClasses[0]:valueClasses;
+			
+			super(valueClasses);
 		}
 		
-		/** @inheritDoc */
-		[ArrayElementType("Class")]
-		public function get valueClasses():Array { return _valueClasses; }
-		
-		public function set valueClasses(value:Array):void
+		/**
+		 * @inheritDoc
+		 * @throws flash.errors.IllegalOperationError <code>IllegalOperationError</code>: You cannot addOnce() then add() the same listener without removing the relationship first.
+		 * @throws ArgumentError <code>ArgumentError</code>: Given listener is <code>null</code>.
+		 */
+		public function add(listener:Function):ISlot
 		{
-			// Clone so the Array cannot be affected from outside.
-			_valueClasses = value ? value.slice() : [];
-			for (var i:int = _valueClasses.length; i--; )
-			{
-				if (!(_valueClasses[i] is Class))
-				{
-					throw new ArgumentError('Invalid valueClasses argument: ' +
-						'item at index ' + i + ' should be a Class but was:<' +
-						_valueClasses[i] + '>.' + getQualifiedClassName(_valueClasses[i]));
-				}
-			}
-		}
-		
-		/** @inheritDoc */
-		public function get numListeners():uint { return bindings.length; }
-		
-		/** @inheritDoc */
-		//TODO: @throws
-		public function add(listener:Function):Function
-		{
-			registerListener(listener);
-			return listener;
-		}
-		
-		/** @inheritDoc */
-		public function addOnce(listener:Function):Function
-		{
-			registerListener(listener, true);
-			return listener;
-		}
-		
-		/** @inheritDoc */
-		public function remove(listener:Function):Function
-		{
-			bindings = bindings.filterNot(listener);
-
-			if (!bindings.nonEmpty) existing = null;
-			else delete existing[listener];
-
-			return listener;
-		}
-		
-		/** @inheritDoc */
-		public function removeAll():void
-		{
-			bindings = SignalBindingList.NIL;
-			existing = null;
-		}
-		
-		/** @inheritDoc */
-		public function dispatch(...valueObjects):void
-		{
-			//
-			// Validate value objects against pre-defined value classes.
-			//
-
-			var valueObject:Object;
-			var valueClass:Class;
-
-			const numValueClasses: int = valueClasses.length;
-			const numValueObjects: int = valueObjects.length;
-
-			if (numValueObjects < numValueClasses)
-			{
-				throw new ArgumentError('Incorrect number of arguments. '+
-					'Expected at least '+numValueClasses+' but received '+
-					numValueObjects+'.');
-			}
-			
-			for (var i: int = 0; i < numValueClasses; ++i)
-			{
-				valueObject = valueObjects[i];
-				valueClass = valueClasses[i];
-
-				if (valueObject === null || valueObject is valueClass) continue;
-					
-				throw new ArgumentError('Value object <'+valueObject
-					+'> is not an instance of <'+valueClass+'>.');
-			}
-
-			//
-			// Broadcast to listeners.
-			//
-
-			var bindingsToProcess:SignalBindingList = bindings;
-
-			if (bindingsToProcess.nonEmpty)
-			{
-				if (numValueObjects == 0)
-				{
-					while (bindingsToProcess.nonEmpty)
-					{
-						bindingsToProcess.head.execute0();
-						bindingsToProcess = bindingsToProcess.tail;
-					}
-				}
-				else if (numValueObjects == 1)
-				{
-					const singleValue:Object = valueObjects[0];
-
-					while (bindingsToProcess.nonEmpty)
-					{
-						bindingsToProcess.head.execute1(singleValue);
-						bindingsToProcess = bindingsToProcess.tail;
-					}
-				}
-				else if (numValueObjects == 2)
-				{
-					const value1:Object = valueObjects[0];
-					const value2:Object = valueObjects[1];
-
-					while (bindingsToProcess.nonEmpty)
-					{
-						bindingsToProcess.head.execute2(value1, value2);
-						bindingsToProcess = bindingsToProcess.tail;
-					}
-				}
-				else
-				{
-					while (bindingsToProcess.nonEmpty)
-					{
-						bindingsToProcess.head.execute(valueObjects);
-						bindingsToProcess = bindingsToProcess.tail;
-					}
-				}
-			}
-		}
-
-		protected function registerListener(listener:Function, once:Boolean = false):void
-		{
-			if (!bindings.nonEmpty || verifyRegistrationOf(listener, once))
-			{
-				bindings = new SignalBindingList(new SignalBinding(listener, once, this), bindings);
-
-				if (null == existing) existing = new Dictionary();
-
-				existing[listener] = true;
-			}
-		}
-
-		protected function verifyRegistrationOf(listener: Function,  once: Boolean): Boolean
-		{
-			if(!existing || !existing[listener]) return true;
-			
-			const existingBinding:ISignalBinding = bindings.find(listener);
-
-			if (null != existingBinding)
-			{
-				if (existingBinding.once != once)
-				{
-					//
-					// If the listener was previously added, definitely don't add it again.
-					// But throw an exception if their once value differs.
-					//
-
-					throw new IllegalOperationError('You cannot addOnce() then add() the same listener without removing the relationship first.');
-				}
-
-				//
-				// Listener was already added.
-				//
-
-				return false;
-			}
-
-			//
-			// This listener has not been added before.
-			//
-			
-			return true;
+			return registerListener(listener);
 		}
 	}
 }

as3signals/src/org/osflash/signals/SignalBinding.as

-package org.osflash.signals 
-{
-    /**
-     * The SignalBinding class represents a signal binding.
-     *
-     * @author Robert Penner
-	 * @author Joa Ebert
-	 * @private
-     */
-	public final class SignalBinding implements ISignalBinding
-	{
-		/**
-		 * Private backing variable for the <code>signal</code> property.
-		 * @private
-		 */
-		private var _signal:ISignal;
-
-		/**
-		 * Private backing variable for the <code>paused</code> property.
-		 * @private
-		 */
-		private var _paused:Boolean;
-
-		/**
-		 * Private backing variable for the <code>listener</code> property.
-		 *
-		 * Visible in the signals package for fast access.
-		 * @private
-		 */
-		private var _listener:Function;
-
-		/**
-		 * Private backing variable for the <code>once</code> property.
-		 *
-		 * Visible in the signals package for fast access.
-		 * @private
-		 */
-		private var _once:Boolean;
-
-		/**
-		 * Private backing variable for the <code>priority</code> property.
-		 *
-		 * Visible in the signals package for fast access.
-		 * @private
-		 */
-		private var _priority:int;
-
-		/**
-		 * Creates and returns a new SignalBinding object.
-		 *
-		 * @param listener The listener associated with the binding.
-		 * @param once Whether or not the listener should be executed only once.
-		 * @param signal The signal associated with the binding.
-		 * @param priority The priority of the binding.
-		 *
-		 * @throws ArgumentError An error is thrown if the given listener closure is <code>null</code>.
-		 */
-		public function SignalBinding(listener:Function, once:Boolean = false, signal:ISignal = null, priority:int = 0)
-		{
-			_listener = listener;
-			_once = once;
-			_signal = signal;
-			_priority = priority;
-
-			verifyListener(listener);
-		}
-
-		/**
-		 * @inheritDoc
-		 */
-		public function execute(valueObjects:Array):void
-		{
-			if (!_paused)
-			{
-				if (_once) remove();
-				_listener.apply(null, valueObjects);
-			}
-		}
-
-		/**
-		 * @inheritDoc
-		 */
-		public function execute0():void
-		{
-			if (!_paused)
-			{
-				if (_once) remove();
-				_listener();
-			}
-		}
-
-		/**
-		 * @inheritDoc
-		 */
-		public function execute1(value1:Object):void
-		{
-			if (!_paused)
-			{
-				if (_once) remove();
-				_listener(value1);
-			}
-		}
-
-		/**
-		 * @inheritDoc
-		 */
-		public function execute2(value1:Object, value2:Object):void
-		{
-			if (!_paused)
-			{
-				if (_once) remove();
-				_listener(value1, value2);
-			}
-		}
-
-		/**
-		 * @inheritDoc
-		 */
-		public function get listener():Function
-		{
-			return _listener;
-		}
-
-		public function set listener(value:Function):void
-		{
-			if (null == value) throw new ArgumentError(
-					'Given listener is null.\nDid you want to call pause() instead?');
-
-			verifyListener(value);
-			_listener = value;
-		}
-
-		/**
-		 * @inheritDoc
-		 */
-		public function get once():Boolean
-		{
-			return _once;
-		}
-
-		/**
-		 * @inheritDoc
-		 */
-		public function get priority():int
-		{
-			return _priority;
-		}
-
-		/**
-		 * Creates and returns the string representation of the current object.
-		 *
-		 * @return The string representation of the current object.
-		 */
-		public function toString():String
-		{
-			return "[SignalBinding listener: "+_listener+", once: "+_once+", priority: "+_priority+", paused: "+_paused+"]";
-		}
-
-		/**
-		 * @inheritDoc
-		 */
-		public function get paused():Boolean
-		{
-			return _paused;
-		}
-
-		public function set paused(value:Boolean):void
-		{
-			_paused = value;
-		}
-
-		/**
-		 * @inheritDoc
-		 */
-		public function pause():void
-		{
-			_paused = true;
-		}
-
-		/**
-		 * @inheritDoc
-		 */
-		public function resume():void
-		{
-			_paused = false;
-		}
-
-		/**
-		 * @inheritDoc
-		 */
-		public function remove():void
-		{
-			_signal.remove(_listener);
-		}
-
-		protected function verifyListener(listener: Function): void
-		{
-			if(null == listener)
-			{
-				throw new ArgumentError('Given listener is null.');
-			}
-
-			if(null == _signal)
-			{
-				throw new Error('Internal signal reference has not been set yet.');
-			}
-			
-			if(listener.length < _signal.valueClasses.length)
-			{
-				const argumentString:String = (listener.length == 1) ? 'argument' : 'arguments';
-
-				throw new ArgumentError('Listener has '+listener.length+' '+argumentString+' but it needs at least '+
-						_signal.valueClasses.length+' to match the signal\'s value classes.');
-			}
-		}
-	}
-}

as3signals/src/org/osflash/signals/SignalBindingList.as

-package org.osflash.signals
-{
-	/**
-	 * The SignalBindingList class represents an immutable list of SignalBinding objects.
-	 *
-	 * @author Joa Ebert
-	 * @private
-	 */
-	public final class SignalBindingList
-	{
-		public static const NIL: SignalBindingList = new SignalBindingList(null, null);
-
-		/**
-		 * Creates and returns a new SignalBindingList object.
-		 *
-		 * <p>A user never has to create a SignalBindingList manually. Use the <code>NIL</code> element to represent an
-		 * empty list. <code>NIL.prepend(value)</code> would create a list containing <code>value</code>.
-		 *
-		 * @param head The head of the list.
-		 * @param tail The tail of the list.
-		 */
-		public function SignalBindingList(head:ISignalBinding, tail:SignalBindingList)
-		{
-			if(null == head && null == tail)
-			{
-				if(null != NIL) throw new ArgumentError(
-						'Parameters head and tail are null. Use the NIL element instead.');
-
-				//this is the NIL element per definition
-				nonEmpty = false;
-			}
-			else
-			{
-				if(null == tail) throw new ArgumentError('Tail must not be null.');
-
-				this.head = head;
-				this.tail = tail;
-				nonEmpty = true;
-			}
-		}
-
-		//
-		// Although those variables are not const, they would be if AS3 would handle it correct.
-		//
-
-		public var head: ISignalBinding;
-		public var tail: SignalBindingList;
-		public var nonEmpty: Boolean;
-
-		/**
-		 * Whether or not the list is empty.
-		 *
-		 * <code>isEmpty</code> is the same as <code>!nonEmpty</code>. If performance is a criteria one should always
-		 * use the <code>nonEmpty</code> method. <code>isEmpty</code> is only a wrapper for convinience.
-		 */
-		public function get isEmpty():Boolean
-		{
-			return !nonEmpty;
-		}
-
-		/**
-		 * The length of the list.
-		 */
-		public function get length():int
-		{
-			if (!nonEmpty) return 0;
-
-			//
-			// We could cache the length, but it would make methods like filterNot unnecessarily complicated.
-			// Instead we assume that O(n) is okay since the length property is used in rare cases.
-			// We could also cache the length lazy, but that is a waste of another 8b per list node (at least).
-			//
-
-			var result: int = 0;
-			var p:SignalBindingList = this;
-
-			while (p.nonEmpty)
-			{
-				++result;
-				p = p.tail;
-			}
-
-			return result;
-		}
-
-		public function prepend(value:SignalBinding):SignalBindingList
-		{
-			return new SignalBindingList(value, this);
-		}
-
-		public function insertWithPriority(value: ISignalBinding):SignalBindingList
-		{
-			if (!nonEmpty) return new SignalBindingList(value, this);
-
-			const priority: int = value.priority;
-
-			if(priority > this.head.priority) return new SignalBindingList(value, this);
-
-			var p:SignalBindingList = this;
-			var q:SignalBindingList = null;
-
-			var first:SignalBindingList = null;
-			var last:SignalBindingList = null;
-
-			while (p.nonEmpty)
-			{
-				if (priority > p.head.priority)
-				{
-					q = new SignalBindingList(value, p);
-
-					if(null != last) last.tail = q;
-
-					return q;
-				}
-				else
-				{
-					q = new SignalBindingList(p.head, NIL);
-
-					if (null != last) last.tail = q;
-					if (null == first) first = q;
-
-					last = q;
-				}
-
-				p = p.tail;
-			}
-
-			if (first == null || last == null) throw new Error('Internal error.');
-
-			last.tail = new SignalBindingList(value, NIL);
-
-			return first;
-		}
-
-		public function filterNot(listener:Function):SignalBindingList
-		{
-			if (!nonEmpty) return this;
-
-			if (listener == head.listener) return tail;
-
-			var p:SignalBindingList = this;
-			var q:SignalBindingList = null;
-
-			var first:SignalBindingList = null;
-			var last:SignalBindingList = null;
-
-			while (p.nonEmpty)
-			{
-				if (p.head.listener != listener)
-				{
-					q = new SignalBindingList(p.head, NIL);
-
-					if (null != last) last.tail = q;
-					if (null == first) first = q;
-
-					last = q;
-				}
-				else
-				{
-					//
-					// No need to check if first == null and last != null
-					// since we check already at the top if listener == head.listener
-					//
-
-					last.tail = p.tail;
-					return first;
-				}
-
-				p = p.tail;
-			}
-
-			return this;
-		}
-
-		public function contains(listener:Function):Boolean
-		{
-			if (!nonEmpty) return false;
-
-			var p:SignalBindingList = this;
-
-			while (p.nonEmpty)
-			{
-				if(p.head.listener == listener) return true;
-
-				p = p.tail;
-			}
-
-			return false;
-		}
-
-		public function find(listener:Function):ISignalBinding
-		{
-			if (!nonEmpty) return null;
-
-			var p:SignalBindingList = this;
-
-			while (p.nonEmpty)
-			{
-				if(p.head.listener == listener) return p.head;
-
-				p = p.tail;
-			}
-
-			return null;
-		}
-
-		public function toString(): String
-		{
-			var buffer:String = '';
-			var p:SignalBindingList = this;
-
-			while (!p.nonEmpty) buffer += p.head+" -> ";
-
-			buffer += "Nil";
-
-			return "[List "+buffer+"]"
-		}
-	}
-}

as3signals/src/org/osflash/signals/Slot.as

+package org.osflash.signals 
+{
+    /**
+     * The Slot class represents a signal slot.
+     *
+     * @author Robert Penner
+	 * @author Joa Ebert
+     */
+	public class Slot implements ISlot
+	{
+		protected var _signal:IOnceSignal;
+		protected var _enabled:Boolean = true;
+		protected var _listener:Function;
+		protected var _once:Boolean = false;
+		protected var _priority:int = 0;
+		protected var _params:Array;
+		
+		/**
+		 * Creates and returns a new Slot object.
+		 *
+		 * @param listener The listener associated with the slot.
+		 * @param signal The signal associated with the slot.
+		 * @param once Whether or not the listener should be executed only once.
+		 * @param priority The priority of the slot.
+		 *
+		 * @throws ArgumentError <code>ArgumentError</code>: Given listener is <code>null</code>.
+		 * @throws Error <code>Error</code>: Internal signal reference has not been set yet.
+		 */
+		public function Slot(listener:Function, signal:IOnceSignal, once:Boolean = false, priority:int = 0)
+		{
+			_listener = listener;
+			_once = once;
+			_signal = signal;
+			_priority = priority;
+							
+			verifyListener(listener);
+		}
+		
+		/**
+		 * @inheritDoc
+		 */
+		public function execute0():void
+		{
+			if (!_enabled) return;
+			if (_once) remove();
+			if (_params && _params.length)
+			{
+				_listener.apply(null, _params);
+				return;
+			}
+			_listener();
+		}		
+		
+		/**
+		 * @inheritDoc
+		 */
+		public function execute1(value:Object):void
+		{
+			if (!_enabled) return;
+			if (_once) remove();
+			if (_params && _params.length)
+			{
+				_listener.apply(null, [value].concat(_params));
+				return;
+			}
+			_listener(value);
+		}		
+
+		/**
+		 * @inheritDoc
+		 */
+		public function execute(valueObjects:Array):void
+		{
+			if (!_enabled) return;
+			if (_once) remove();
+			
+			// If we have parameters, add them to the valueObject
+			// Note: This could be expensive if we're after the fastest dispatch possible.
+			if (_params && _params.length)
+			{
+				valueObjects = valueObjects.concat(_params);
+			}
+			
+			// NOTE: simple ifs are faster than switch: http://jacksondunstan.com/articles/1007
+			const numValueObjects:int = valueObjects.length;
+			if (numValueObjects == 0)
+			{
+				_listener();
+			}
+			else if (numValueObjects == 1)
+			{
+				_listener(valueObjects[0]);
+			}
+			else if (numValueObjects == 2)
+			{
+				_listener(valueObjects[0], valueObjects[1]);
+			}
+			else if (numValueObjects == 3)
+			{
+				_listener(valueObjects[0], valueObjects[1], valueObjects[2]);
+			}
+			else
+			{
+				_listener.apply(null, valueObjects);
+			}
+		}
+
+		/**
+		 * @inheritDoc
+		 * @throws ArgumentError <code>ArgumentError</code>: Given listener is <code>null</code>. Did you want to set enabled to false instead?
+		 * @throws Error <code>Error</code>: Internal signal reference has not been set yet.
+		 */
+		public function get listener():Function
+		{
+			return _listener;
+		}
+
+		public function set listener(value:Function):void
+		{
+			if (null == value) throw new ArgumentError(
+					'Given listener is null.\nDid you want to set enabled to false instead?');
+			
+			verifyListener(value);
+			_listener = value;
+		}
+
+		/**
+		 * @inheritDoc
+		 */
+		public function get once():Boolean { return _once; }
+
+		/**
+		 * @inheritDoc
+		 */
+		public function get priority():int { return _priority; }
+
+		/**
+		 * Creates and returns the string representation of the current object.
+		 *
+		 * @return The string representation of the current object.
+		 */
+		public function toString():String
+		{
+			return "[Slot listener: "+_listener+", once: "+_once
+											+", priority: "+_priority+", enabled: "+_enabled+"]";
+		}
+
+		/**
+		 * @inheritDoc
+		 */
+		public function get enabled():Boolean { return _enabled; }
+
+		public function set enabled(value:Boolean):void	{ _enabled = value; }
+		
+		/**
+		 * @inheritDoc
+		 */		
+		public function get params():Array { return _params; }
+		
+		public function set params(value:Array):void { _params = value; }
+		
+		/**
+		 * @inheritDoc
+		 */
+		public function remove():void
+		{
+			_signal.remove(_listener);
+		}
+
+		protected function verifyListener(listener:Function): void
+		{
+			if (null == listener)
+			{
+				throw new ArgumentError('Given listener is null.');
+			}
+
+			if (null == _signal)
+			{
+				throw new Error('Internal signal reference has not been set yet.');
+			}
+			
+		}
+	}
+}

as3signals/src/org/osflash/signals/SlotList.as

+package org.osflash.signals
+{
+	/**
+	 * The SlotList class represents an immutable list of Slot objects.
+	 *
+	 * @author Joa Ebert
+	 * @author Robert Penner
+	 */
+	public final class SlotList
+	{
+		/**
+		 * Represents an empty list. Used as the list terminator.
+		 */
+		public static const NIL:SlotList = new SlotList(null, null);
+
+		/**
+		 * Creates and returns a new SlotList object.
+		 *
+		 * <p>A user never has to create a SlotList manually. 
+		 * Use the <code>NIL</code> element to represent an empty list. 
+		 * <code>NIL.prepend(value)</code> would create a list containing <code>value</code></p>.
+		 *
+		 * @param head The first slot in the list.
+		 * @param tail A list containing all slots except head.
+		 * 
+		 * @throws ArgumentError <code>ArgumentError</code>: Parameters head and tail are null. Use the NIL element instead.
+		 * @throws ArgumentError <code>ArgumentError</code>: Parameter head cannot be null.
+		 */
+		public function SlotList(head:ISlot, tail:SlotList = null)
+		{
+			if (!head && !tail)
+			{
+				if (NIL) 
+					throw new ArgumentError('Parameters head and tail are null. Use the NIL element instead.');
+					
+				//this is the NIL element as per definition
+				nonEmpty = false;
+			}
+			else if (!head)
+			{
+				throw new ArgumentError('Parameter head cannot be null.');
+			}
+			else
+			{
+				this.head = head;
+				this.tail = tail || NIL;
+				nonEmpty = true;
+			}
+		}
+
+		// Although those variables are not const, they would be if AS3 would handle it correctly.
+		public var head:ISlot;
+		public var tail:SlotList;
+		public var nonEmpty:Boolean = false;
+
+		/**
+		 * The number of slots in the list.
+		 */
+		public function get length():uint
+		{
+			if (!nonEmpty) return 0;
+			if (tail == NIL) return 1;
+
+			// We could cache the length, but it would make methods like filterNot unnecessarily complicated.
+			// Instead we assume that O(n) is okay since the length property is used in rare cases.
+			// We could also cache the length lazy, but that is a waste of another 8b per list node (at least).
+
+			var result:uint = 0;
+			var p:SlotList = this;
+
+			while (p.nonEmpty)
+			{
+				++result;
+				p = p.tail;
+			}
+
+			return result;
+		}
+		
+		/**
+		 * Prepends a slot to this list.
+		 * @param	slot The item to be prepended.
+		 * @return	A list consisting of slot followed by all elements of this list.
+		 * 
+		 * @throws ArgumentError <code>ArgumentError</code>: Parameter head cannot be null.
+		 */
+		public function prepend(slot:ISlot):SlotList
+		{
+			return new SlotList(slot, this);
+		}
+
+		/**
+		 * Appends a slot to this list.
+		 * Note: appending is O(n). Where possible, prepend which is O(1).
+		 * In some cases, many list items must be cloned to 
+		 * avoid changing existing lists.
+		 * @param	slot The item to be appended.
+		 * @return	A list consisting of all elements of this list followed by slot.
+		 */
+		public function append(slot:ISlot):SlotList
+		{
+			if (!slot) return this;
+			if (!nonEmpty) return new SlotList(slot);
+			// Special case: just one slot currently in the list.
+			if (tail == NIL) 
+				return new SlotList(slot).prepend(head);
+			
+			// The list already has two or more slots.
+			// We have to build a new list with cloned items because they are immutable.
+			const wholeClone:SlotList = new SlotList(head);
+			var subClone:SlotList = wholeClone;
+			var current:SlotList = tail;
+
+			while (current.nonEmpty)
+			{