as3signals 0.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (56) hide show
  1. data/CHANGELOG.textile +51 -0
  2. data/Gemfile +6 -0
  3. data/MIT-LICENSE.txt +22 -0
  4. data/README.textile +43 -0
  5. data/Rakefile +52 -0
  6. data/as3-signals.as3proj +87 -0
  7. data/as3-signals.docproj +9 -0
  8. data/as3signals.gemspec +24 -0
  9. data/build-asunit.properties +7 -0
  10. data/build-asunit.xml +34 -0
  11. data/build.properties +20 -0
  12. data/build.xml +53 -0
  13. data/lib/as3signals.rb +15 -0
  14. data/src/org/osflash/signals/DeluxeSignal.as +260 -0
  15. data/src/org/osflash/signals/IDeluxeSignal.as +34 -0
  16. data/src/org/osflash/signals/IDispatcher.as +15 -0
  17. data/src/org/osflash/signals/ISignal.as +44 -0
  18. data/src/org/osflash/signals/ISignalOwner.as +13 -0
  19. data/src/org/osflash/signals/Signal.as +206 -0
  20. data/src/org/osflash/signals/events/GenericEvent.as +44 -0
  21. data/src/org/osflash/signals/events/IBubbleEventHandler.as +14 -0
  22. data/src/org/osflash/signals/events/IEvent.as +27 -0
  23. data/src/org/osflash/signals/natives/INativeDispatcher.as +34 -0
  24. data/src/org/osflash/signals/natives/NativeMappedSignal.as +230 -0
  25. data/src/org/osflash/signals/natives/NativeRelaySignal.as +71 -0
  26. data/src/org/osflash/signals/natives/NativeSignal.as +176 -0
  27. data/tests/org/osflash/signals/AllTests.as +44 -0
  28. data/tests/org/osflash/signals/AllTestsRunner.as +19 -0
  29. data/tests/org/osflash/signals/AmbiguousRelationshipTest.as +60 -0
  30. data/tests/org/osflash/signals/DeluxeSignalAmbiguousRelationshipTest.as +60 -0
  31. data/tests/org/osflash/signals/DeluxeSignalDispatchExtraArgsTest.as +43 -0
  32. data/tests/org/osflash/signals/DeluxeSignalDispatchNoArgsTest.as +55 -0
  33. data/tests/org/osflash/signals/DeluxeSignalDispatchNonEventTest.as +67 -0
  34. data/tests/org/osflash/signals/DeluxeSignalSplitInterfacesTest.as +41 -0
  35. data/tests/org/osflash/signals/DeluxeSignalTest.as +134 -0
  36. data/tests/org/osflash/signals/DeluxeSignalWithBubblingEventTest.as +129 -0
  37. data/tests/org/osflash/signals/DeluxeSignalWithCustomEventTest.as +84 -0
  38. data/tests/org/osflash/signals/DeluxeSignalWithGenericEventTest.as +190 -0
  39. data/tests/org/osflash/signals/GenericEventTest.as +62 -0
  40. data/tests/org/osflash/signals/PriorityListenersTest.as +68 -0
  41. data/tests/org/osflash/signals/RedispatchedEventTest.as +51 -0
  42. data/tests/org/osflash/signals/SignalDispatchArgsTest.as +74 -0
  43. data/tests/org/osflash/signals/SignalDispatchNoArgsTest.as +55 -0
  44. data/tests/org/osflash/signals/SignalDispatchNonEventTest.as +81 -0
  45. data/tests/org/osflash/signals/SignalSplitInterfacesTest.as +47 -0
  46. data/tests/org/osflash/signals/SignalTest.as +267 -0
  47. data/tests/org/osflash/signals/SignalWithCustomEventTest.as +107 -0
  48. data/tests/org/osflash/signals/natives/AmbiguousRelationshipInNativeSignalTest.as +63 -0
  49. data/tests/org/osflash/signals/natives/NativeMappedSignalBoundaryUseTest.as +100 -0
  50. data/tests/org/osflash/signals/natives/NativeMappedSignalDefaultsTest.as +78 -0
  51. data/tests/org/osflash/signals/natives/NativeMappedSignalFunctionArgTest.as +148 -0
  52. data/tests/org/osflash/signals/natives/NativeMappedSignalFunctionNoArgsTest.as +95 -0
  53. data/tests/org/osflash/signals/natives/NativeMappedSignalObjectArgTest.as +85 -0
  54. data/tests/org/osflash/signals/natives/NativeRelaySignalTest.as +174 -0
  55. data/tests/org/osflash/signals/natives/NativeSignalTest.as +312 -0
  56. metadata +152 -0
@@ -0,0 +1,260 @@
1
+ package org.osflash.signals
2
+ {
3
+ import flash.errors.IllegalOperationError;
4
+
5
+ import org.osflash.signals.events.IBubbleEventHandler;
6
+ import org.osflash.signals.events.IEvent;
7
+
8
+ /**
9
+ * Signal dispatches events to multiple listeners.
10
+ * It is inspired by C# events and delegates, and by
11
+ * <a target="_top" href="http://en.wikipedia.org/wiki/Signals_and_slots">signals and slots</a>
12
+ * in Qt.
13
+ * A Signal adds event dispatching functionality through composition and interfaces,
14
+ * rather than inheriting from a dispatcher.
15
+ * <br/><br/>
16
+ * Project home: <a target="_top" href="http://github.com/robertpenner/as3-signals/">http://github.com/robertpenner/as3-signals/</a>
17
+ */
18
+ public class DeluxeSignal implements IDeluxeSignal, ISignalOwner, IDispatcher
19
+ {
20
+ protected var _target:Object;
21
+ protected var _valueClasses:Array;
22
+ protected var listenerBoxes:Array;
23
+ protected var listenersNeedCloning:Boolean = false;
24
+
25
+ /**
26
+ * Creates a DeluxeSignal instance to dispatch events on behalf of a target object.
27
+ * @param target The object the signal is dispatching events on behalf of.
28
+ * @param valueClasses Any number of class references that enable type checks in dispatch().
29
+ * For example, new DeluxeSignal(this, String, uint)
30
+ * would allow: signal.dispatch("the Answer", 42)
31
+ * but not: signal.dispatch(true, 42.5)
32
+ * nor: signal.dispatch()
33
+ *
34
+ * NOTE: Subclasses cannot call super.apply(null, valueClasses),
35
+ * but this constructor has logic to support super(valueClasses).
36
+ */
37
+ public function DeluxeSignal(target:Object, ...valueClasses)
38
+ {
39
+ _target = target;
40
+ listenerBoxes = [];
41
+ // Cannot use super.apply(null, valueClasses), so allow the subclass to call super(valueClasses).
42
+ if (valueClasses.length == 1 && valueClasses[0] is Array)
43
+ valueClasses = valueClasses[0];
44
+ setValueClasses(valueClasses);
45
+ }
46
+
47
+ /** @inheritDoc */
48
+ public function get valueClasses():Array { return _valueClasses; }
49
+
50
+ /** @inheritDoc */
51
+ public function get numListeners():uint { return listenerBoxes.length; }
52
+
53
+ /** @inheritDoc */
54
+ public function get target():Object { return _target; }
55
+
56
+ /** @inheritDoc */
57
+ public function set target(value:Object):void
58
+ {
59
+ if (value == _target) return;
60
+ removeAll();
61
+ _target = value;
62
+ }
63
+
64
+ /** @inheritDoc */
65
+ //TODO: @throws
66
+ public function add(listener:Function):Function
67
+ {
68
+ return addWithPriority(listener)
69
+ }
70
+
71
+ public function addWithPriority(listener:Function, priority:int = 0):Function
72
+ {
73
+ registerListener(listener, false, priority);
74
+ return listener;
75
+ }
76
+
77
+ public function addOnce(listener:Function):Function
78
+ {
79
+ return addOnceWithPriority(listener)
80
+ }
81
+
82
+ /** @inheritDoc */
83
+ public function addOnceWithPriority(listener:Function, priority:int = 0):Function
84
+ {
85
+ registerListener(listener, true, priority);
86
+ return listener;
87
+ }
88
+
89
+ /** @inheritDoc */
90
+ public function remove(listener:Function):Function
91
+ {
92
+ if (indexOfListener(listener) == -1) return listener;
93
+ if (listenersNeedCloning)
94
+ {
95
+ listenerBoxes = listenerBoxes.slice();
96
+ listenersNeedCloning = false;
97
+ }
98
+ listenerBoxes.splice(indexOfListener(listener), 1);
99
+ return listener;
100
+ }
101
+
102
+ /** @inheritDoc */
103
+ public function removeAll():void
104
+ {
105
+ // Looping backwards is more efficient when removing array items.
106
+ for (var i:uint = listenerBoxes.length; i--; )
107
+ {
108
+ remove(listenerBoxes[i].listener as Function);
109
+ }
110
+ }
111
+
112
+ /** @inheritDoc */
113
+ public function dispatch(...valueObjects):void
114
+ {
115
+ // Validate value objects against pre-defined value classes.
116
+ var valueObject:Object;
117
+ var valueClass:Class;
118
+ var len:int = _valueClasses.length;
119
+ for (var i:int = 0; i < len; i++)
120
+ {
121
+ // null is allowed to pass through.
122
+ if ( (valueObject = valueObjects[i]) === null
123
+ || valueObject is (valueClass = _valueClasses[i]) )
124
+ continue;
125
+
126
+ throw new ArgumentError('Value object <' + valueObject
127
+ + '> is not an instance of <' + valueClass + '>.');
128
+ }
129
+
130
+ var event:IEvent = valueObjects[0] as IEvent;
131
+ if (event)
132
+ {
133
+ // clone re-dispatched event
134
+ if (event.target)
135
+ {
136
+ valueObjects[0] = event = event.clone();
137
+ }
138
+ event.target = this.target;
139
+ event.currentTarget = this.target;
140
+ event.signal = this;
141
+ }
142
+
143
+ // During a dispatch, add() and remove() should clone listeners array instead of modifying it.
144
+ listenersNeedCloning = true;
145
+ //// Call listeners.
146
+ var listener:Function;
147
+ if (listenerBoxes.length)
148
+ {
149
+ //TODO: investigate performance of various approaches
150
+
151
+ for each (var listenerBox:Object in listenerBoxes)
152
+ {
153
+ listener = listenerBox.listener;
154
+ if (listenerBox.once) remove(listener);
155
+ listener.apply(null, valueObjects);
156
+ }
157
+ }
158
+
159
+ listenersNeedCloning = false;
160
+
161
+ if (!event || !event.bubbles) return;
162
+
163
+ //// Bubble the event as far as possible.
164
+ var currentTarget:Object = this.target;
165
+ while ( currentTarget && currentTarget.hasOwnProperty("parent")
166
+ && (currentTarget = currentTarget.parent) )
167
+ {
168
+ if (currentTarget is IBubbleEventHandler)
169
+ {
170
+ // onEventBubbled() can stop the bubbling by returning false.
171
+ if (!IBubbleEventHandler(event.currentTarget = currentTarget).onEventBubbled(event))
172
+ break;
173
+ }
174
+ }
175
+ }
176
+
177
+ protected function indexOfListener(listener:Function):int
178
+ {
179
+ for (var i:int = listenerBoxes.length; i--; )
180
+ {
181
+ if (listenerBoxes[i].listener == listener) return i;
182
+ }
183
+ return -1;
184
+ }
185
+
186
+ protected function setValueClasses(valueClasses:Array):void
187
+ {
188
+ _valueClasses = valueClasses || [];
189
+
190
+ for (var i:int = _valueClasses.length; i--; )
191
+ {
192
+ if (!(_valueClasses[i] is Class))
193
+ {
194
+ throw new ArgumentError('Invalid valueClasses argument: item at index ' + i
195
+ + ' should be a Class but was:<' + _valueClasses[i] + '>.');
196
+ }
197
+ }
198
+ }
199
+
200
+ protected function registerListener(listener:Function, once:Boolean = false, priority:int = 0):void
201
+ {
202
+ // function.length is the number of arguments.
203
+ if (listener.length < _valueClasses.length)
204
+ {
205
+ var argumentString:String = (listener.length == 1) ? 'argument' : 'arguments';
206
+ throw new ArgumentError('Listener has '+listener.length+' '+argumentString+' but it needs at least '+_valueClasses.length+' to match the given value classes.');
207
+ }
208
+
209
+ var listenerBox:Object = { listener:listener, once:once, priority:priority };
210
+ // Process the first listener as quickly as possible.
211
+ if (!listenerBoxes.length)
212
+ {
213
+ listenerBoxes[0] = listenerBox;
214
+ return;
215
+ }
216
+
217
+ var prevListenerIndex:int = indexOfListener(listener);
218
+ if (prevListenerIndex >= 0)
219
+ {
220
+ // If the listener was previously added, definitely don't add it again.
221
+ // But throw an exception in some cases, as the error messages explain.
222
+ var prevListenerBox:Object = listenerBoxes[prevListenerIndex];
223
+ if (prevListenerBox.once && !once)
224
+ {
225
+ throw new IllegalOperationError('You cannot addOnce() then add() the same listener without removing the relationship first.');
226
+ }
227
+ else if (!prevListenerBox.once && once)
228
+ {
229
+ throw new IllegalOperationError('You cannot add() then addOnce() the same listener without removing the relationship first.');
230
+ }
231
+ // Listener was already added, so do nothing.
232
+ return;
233
+ }
234
+
235
+ if (listenersNeedCloning)
236
+ {
237
+ listenerBoxes = listenerBoxes.slice();
238
+ listenersNeedCloning = false;
239
+ }
240
+
241
+ // Assume the listeners are already sorted by priority
242
+ // and insert in the right spot. For listeners with the same priority,
243
+ // we must preserve the order in which they were added.
244
+ var len:int = listenerBoxes.length;
245
+ for (var i:int = 0; i < len; i++)
246
+ {
247
+ // As soon as a lower-priority listener is found, go in front of it.
248
+ if (priority > listenerBoxes[i].priority)
249
+ {
250
+ listenerBoxes.splice(i, 0, listenerBox);
251
+ return;
252
+ }
253
+ }
254
+
255
+ // If we made it this far, the new listener has lowest priority, so put it last.
256
+ listenerBoxes[listenerBoxes.length] = listenerBox;
257
+ }
258
+
259
+ }
260
+ }
@@ -0,0 +1,34 @@
1
+ package org.osflash.signals
2
+ {
3
+ /**
4
+ *
5
+ */
6
+ public interface IDeluxeSignal extends ISignal
7
+ {
8
+ /**
9
+ * Subscribes a listener for the signal.
10
+ * After you successfully register an event listener,
11
+ * you cannot change its priority through additional calls to add().
12
+ * To change a listener's priority, you must first call remove().
13
+ * Then you can register the listener again with the new priority level.
14
+ * @param listener A function with an argument
15
+ * that matches the type of event dispatched by the signal.
16
+ * If eventClass is not specified, the listener and dispatch() can be called without an argument.
17
+ */
18
+ function addWithPriority(listener:Function, priority:int = 0):Function
19
+
20
+ /**
21
+ * Subscribes a one-time listener for this signal.
22
+ * The signal will remove the listener automatically the first time it is called,
23
+ * after the dispatch to all listeners is complete.
24
+ * @param listener A function with an argument
25
+ * that matches the type of event dispatched by the signal.
26
+ * If eventClass is not specified, the listener and dispatch() can be called without an argument.
27
+ * @param priority The priority level of the event listener.
28
+ * The priority is designated by a signed 32-bit integer.
29
+ * The higher the number, the higher the priority.
30
+ * All listeners with priority n are processed before listeners of priority n-1.
31
+ */
32
+ function addOnceWithPriority(listener:Function, priority:int = 0):Function
33
+ }
34
+ }
@@ -0,0 +1,15 @@
1
+ package org.osflash.signals
2
+ {
3
+ /**
4
+ *
5
+ */
6
+ public interface IDispatcher
7
+ {
8
+ /**
9
+ * Dispatches an object to listeners.
10
+ * @param valueObjects Any number of parameters to send to listeners. Will be type-checked against valueClasses.
11
+ * @throws ArgumentError <code>ArgumentError</code>: valueObjects are not compatible with valueClasses.
12
+ */
13
+ function dispatch(...valueObjects):void;
14
+ }
15
+ }
@@ -0,0 +1,44 @@
1
+ package org.osflash.signals
2
+ {
3
+ /**
4
+ *
5
+ */
6
+ public interface ISignal
7
+ {
8
+ /**
9
+ * An optional array of classes defining the types of parameters sent to listeners.
10
+ */
11
+ function get valueClasses():Array;
12
+
13
+ /** The current number of listeners for the signal. */
14
+ function get numListeners():uint;
15
+
16
+ /**
17
+ * Subscribes a listener for the signal.
18
+ * @param listener A function with arguments
19
+ * that matches the value classes dispatched by the signal.
20
+ * If value classes are not specified (e.g. via Signal constructor), dispatch() can be called without arguments.
21
+ * @return the listener Function passed as the parameter
22
+ */
23
+ function add(listener:Function):Function;
24
+
25
+ /**
26
+ * Subscribes a one-time listener for this signal.
27
+ * The signal will remove the listener automatically the first time it is called,
28
+ * after the dispatch to all listeners is complete.
29
+ * @param listener A function with arguments
30
+ * that matches the value classes dispatched by the signal.
31
+ * If value classes are not specified (e.g. via Signal constructor), dispatch() can be called without arguments.
32
+ * @return the listener Function passed as the parameter
33
+ */
34
+ function addOnce(listener:Function):Function;
35
+
36
+ /**
37
+ * Unsubscribes a listener from the signal.
38
+ * @param listener
39
+ * @return the listener Function passed as the parameter
40
+ */
41
+ function remove(listener:Function):Function;
42
+
43
+ }
44
+ }
@@ -0,0 +1,13 @@
1
+ package org.osflash.signals
2
+ {
3
+ /**
4
+ * ISignalOwner gives access to the powerful function, removeAll. This should only be called by trusted classes.
5
+ */
6
+ public interface ISignalOwner extends ISignal
7
+ {
8
+ /**
9
+ * Unsubscribes all listeners from the signal.
10
+ */
11
+ function removeAll():void
12
+ }
13
+ }
@@ -0,0 +1,206 @@
1
+ package org.osflash.signals
2
+ {
3
+ import flash.errors.IllegalOperationError;
4
+ import flash.utils.Dictionary;
5
+
6
+ /**
7
+ * Signal dispatches events to multiple listeners.
8
+ * It is inspired by C# events and delegates, and by
9
+ * <a target="_top" href="http://en.wikipedia.org/wiki/Signals_and_slots">signals and slots</a>
10
+ * in Qt.
11
+ * A Signal adds event dispatching functionality through composition and interfaces,
12
+ * rather than inheriting from a dispatcher.
13
+ * <br/><br/>
14
+ * Project home: <a target="_top" href="http://github.com/robertpenner/as3-signals/">http://github.com/robertpenner/as3-signals/</a>
15
+ */
16
+ public class Signal implements ISignalOwner, IDispatcher
17
+ {
18
+ protected var _valueClasses:Array; // of Class
19
+ protected var listeners:Array; // of Function
20
+ protected var onceListeners:Dictionary; // of Function
21
+ protected var listenersNeedCloning:Boolean = false;
22
+
23
+ /**
24
+ * Creates a Signal instance to dispatch value objects.
25
+ * @param valueClasses Any number of class references that enable type checks in dispatch().
26
+ * For example, new Signal(String, uint)
27
+ * would allow: signal.dispatch("the Answer", 42)
28
+ * but not: signal.dispatch(true, 42.5)
29
+ * nor: signal.dispatch()
30
+ *
31
+ * NOTE: Subclasses cannot call super.apply(null, valueClasses),
32
+ * but this constructor has logic to support super(valueClasses).
33
+ */
34
+ public function Signal(...valueClasses)
35
+ {
36
+ listeners = [];
37
+ onceListeners = new Dictionary();
38
+ // Cannot use super.apply(null, valueClasses), so allow the subclass to call super(valueClasses).
39
+ if (valueClasses.length == 1 && valueClasses[0] is Array)
40
+ valueClasses = valueClasses[0];
41
+ setValueClasses(valueClasses);
42
+ }
43
+
44
+ /** @inheritDoc */
45
+ public function get valueClasses():Array { return _valueClasses; }
46
+
47
+ /** @inheritDoc */
48
+ public function get numListeners():uint { return listeners.length; }
49
+
50
+ /** @inheritDoc */
51
+ //TODO: @throws
52
+ public function add(listener:Function):Function
53
+ {
54
+ registerListener(listener);
55
+ return listener;
56
+ }
57
+
58
+ /** @inheritDoc */
59
+ public function addOnce(listener:Function):Function
60
+ {
61
+ registerListener(listener, true);
62
+ return listener;
63
+ }
64
+
65
+ /** @inheritDoc */
66
+ public function remove(listener:Function):Function
67
+ {
68
+ var index:int = listeners.indexOf(listener);
69
+ if (index == -1) return listener;
70
+ if (listenersNeedCloning)
71
+ {
72
+ listeners = listeners.slice();
73
+ listenersNeedCloning = false;
74
+ }
75
+ listeners.splice(index, 1);
76
+ delete onceListeners[listener];
77
+ return listener;
78
+ }
79
+
80
+ /** @inheritDoc */
81
+ public function removeAll():void
82
+ {
83
+ // Looping backwards is more efficient when removing array items.
84
+ for (var i:uint = listeners.length; i--; )
85
+ {
86
+ remove(listeners[i] as Function);
87
+ }
88
+ }
89
+
90
+ /** @inheritDoc */
91
+ public function dispatch(...valueObjects):void
92
+ {
93
+ // Validate value objects against pre-defined value classes.
94
+ var valueObject:Object;
95
+ var valueClass:Class;
96
+ var numValueClasses:int = _valueClasses.length;
97
+ if (valueObjects.length < numValueClasses)
98
+ {
99
+ throw new ArgumentError('Incorrect number of arguments. Expected at least ' + numValueClasses + ' but received ' + valueObjects.length + '.');
100
+ }
101
+
102
+ for (var i:int = 0; i < numValueClasses; i++)
103
+ {
104
+ // null is allowed to pass through.
105
+ if ( (valueObject = valueObjects[i]) === null
106
+ || valueObject is (valueClass = _valueClasses[i]) )
107
+ continue;
108
+
109
+ throw new ArgumentError('Value object <' + valueObject
110
+ + '> is not an instance of <' + valueClass + '>.');
111
+ }
112
+
113
+ if (!listeners.length) return;
114
+
115
+ //// Call listeners.
116
+
117
+ // During a dispatch, add() and remove() should clone listeners array instead of modifying it.
118
+ listenersNeedCloning = true;
119
+ var listener:Function;
120
+ switch (valueObjects.length)
121
+ {
122
+ case 0:
123
+ for each (listener in listeners)
124
+ {
125
+ if (onceListeners[listener]) remove(listener);
126
+ listener();
127
+ }
128
+ break;
129
+
130
+ case 1:
131
+ for each (listener in listeners)
132
+ {
133
+ if (onceListeners[listener]) remove(listener);
134
+ listener(valueObjects[0]);
135
+ }
136
+ break;
137
+
138
+ default:
139
+ for each (listener in listeners)
140
+ {
141
+ if (onceListeners[listener]) remove(listener);
142
+ listener.apply(null, valueObjects);
143
+ }
144
+ }
145
+ listenersNeedCloning = false;
146
+ }
147
+
148
+ protected function setValueClasses(valueClasses:Array):void
149
+ {
150
+ _valueClasses = valueClasses || [];
151
+
152
+ for (var i:int = _valueClasses.length; i--; )
153
+ {
154
+ if (!(_valueClasses[i] is Class))
155
+ {
156
+ throw new ArgumentError('Invalid valueClasses argument: item at index ' + i
157
+ + ' should be a Class but was:<' + _valueClasses[i] + '>.');
158
+ }
159
+ }
160
+ }
161
+
162
+ protected function registerListener(listener:Function, once:Boolean = false):void
163
+ {
164
+ // function.length is the number of arguments.
165
+ if (listener.length < _valueClasses.length)
166
+ {
167
+ var argumentString:String = (listener.length == 1) ? 'argument' : 'arguments';
168
+ throw new ArgumentError('Listener has '+listener.length+' '+argumentString+' but it needs at least '+_valueClasses.length+' to match the given value classes.');
169
+ }
170
+
171
+ // If there are no previous listeners, add the first one as quickly as possible.
172
+ if (!listeners.length)
173
+ {
174
+ listeners[0] = listener;
175
+ if (once) onceListeners[listener] = true;
176
+ return;
177
+ }
178
+
179
+ if (listeners.indexOf(listener) >= 0)
180
+ {
181
+ // If the listener was previously added, definitely don't add it again.
182
+ // But throw an exception in some cases, as the error messages explain.
183
+ if (onceListeners[listener] && !once)
184
+ {
185
+ throw new IllegalOperationError('You cannot addOnce() then add() the same listener without removing the relationship first.');
186
+ }
187
+ else if (!onceListeners[listener] && once)
188
+ {
189
+ throw new IllegalOperationError('You cannot add() then addOnce() the same listener without removing the relationship first.');
190
+ }
191
+ // Listener was already added, so do nothing.
192
+ return;
193
+ }
194
+
195
+ if (listenersNeedCloning)
196
+ {
197
+ listeners = listeners.slice();
198
+ listenersNeedCloning = false;
199
+ }
200
+
201
+ // Faster than push().
202
+ listeners[listeners.length] = listener;
203
+ if (once) onceListeners[listener] = true;
204
+ }
205
+ }
206
+ }
@@ -0,0 +1,44 @@
1
+ package org.osflash.signals.events
2
+ {
3
+ import org.osflash.signals.IDeluxeSignal;
4
+
5
+ /**
6
+ *
7
+ * @see org.osflash.signals.events.IEvent
8
+ * Documentation for the event interface being maintained in IEvent to avoid duplication for now.
9
+ */
10
+ public class GenericEvent implements IEvent
11
+ {
12
+ protected var _bubbles:Boolean;
13
+ protected var _target:Object;
14
+ protected var _currentTarget:Object;
15
+ protected var _signal:IDeluxeSignal;
16
+
17
+ public function GenericEvent(bubbles:Boolean = false)
18
+ {
19
+ _bubbles = bubbles;
20
+ }
21
+
22
+ /** @inheritDoc */
23
+ public function get signal():IDeluxeSignal { return _signal; }
24
+ public function set signal(value:IDeluxeSignal):void { _signal = value; }
25
+
26
+ /** @inheritDoc */
27
+ public function get target():Object { return _target; }
28
+ public function set target(value:Object):void { _target = value; }
29
+
30
+ /** @inheritDoc */
31
+ public function get currentTarget():Object { return _currentTarget; }
32
+ public function set currentTarget(value:Object):void { _currentTarget = value; }
33
+
34
+ /** @inheritDoc */
35
+ public function get bubbles():Boolean { return _bubbles; }
36
+ public function set bubbles(value:Boolean):void { _bubbles = value; }
37
+
38
+ /** @inheritDoc */
39
+ public function clone():IEvent
40
+ {
41
+ return new GenericEvent(_bubbles);
42
+ }
43
+ }
44
+ }
@@ -0,0 +1,14 @@
1
+ package org.osflash.signals.events
2
+ {
3
+
4
+ public interface IBubbleEventHandler
5
+ {
6
+ /**
7
+ * Handler for event bubbling.
8
+ * It's left to the IBubbleEventHandler to decide what to do with the event.
9
+ * @param event The event that bubbled up.
10
+ * @return whether to continue bubbling this event
11
+ */
12
+ function onEventBubbled(event:IEvent):Boolean;
13
+ }
14
+ }
@@ -0,0 +1,27 @@
1
+ package org.osflash.signals.events
2
+ {
3
+ import org.osflash.signals.IDeluxeSignal;
4
+
5
+ public interface IEvent
6
+ {
7
+ /** The object that originally dispatched the event.
8
+ * When dispatched from an signal, the target is the object containing the signal. */
9
+ function get target():Object;
10
+ function set target(value:Object):void;
11
+
12
+ /** The object that added the listener for the event. */
13
+ function get currentTarget():Object;
14
+ function set currentTarget(value:Object):void;
15
+
16
+ /** The signal that dispatched the event. */
17
+ function get signal():IDeluxeSignal;
18
+ function set signal(value:IDeluxeSignal):void;
19
+
20
+ /** Indicates whether the event is a bubbling event. */
21
+ function get bubbles():Boolean;
22
+ function set bubbles(value:Boolean):void;
23
+
24
+ /** Returns a new copy of the instance. */
25
+ function clone():IEvent;
26
+ }
27
+ }
@@ -0,0 +1,34 @@
1
+ package org.osflash.signals.natives
2
+ {
3
+ import flash.events.Event;
4
+ import flash.events.IEventDispatcher;
5
+
6
+ /**
7
+ * Similar to IDispatcher but using strong types specific to Flash's native event system.
8
+ */
9
+ public interface INativeDispatcher
10
+ {
11
+ /**
12
+ * The type of event permitted to be dispatched. Corresponds to flash.events.Event.type.
13
+ */
14
+ function get eventType():String;
15
+
16
+ /**
17
+ * The class of event permitted to be dispatched. Will be flash.events.Event or a subclass.
18
+ */
19
+ function get eventClass():Class;
20
+
21
+ /**
22
+ * The object considered the source of the dispatched events.
23
+ */
24
+ function get target():IEventDispatcher;
25
+
26
+ /**
27
+ * Dispatches an event to listeners.
28
+ * @param event An instance of a class that is or extends flash.events.Event.
29
+ * @throws ArgumentError <code>ArgumentError</code>: Event object [event] is not an instance of [eventClass].
30
+ * @throws ArgumentError <code>ArgumentError</code>: Event object has incorrect type. Expected [eventType] but was [event.type].
31
+ */
32
+ function dispatch(event:Event):Boolean;
33
+ }
34
+ }