Tutorials, extensions, and source files for ActionScript, Flash, and other Adobe products.

 

senocular.com ActionScript Library

Sequence.as

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
package com.senocular.gyro {
	
	import flash.events.Event;
	import flash.events.EventDispatcher;
	import flash.events.IEventDispatcher;
	
	/**
	 * Creates a sequence of method items that are called in sequence based 
	 * upon completion events.
	 */
	public class Sequence
		extends EventDispatcher
		implements IStartable {
		
		protected var items:Array = new Array();	// list of SequenceItem objects
		protected var _position:int = 0;			// _position within items
		protected var _currentItem:SequenceItem;	// current event
		protected var nextItem:SequenceItem;		// next event to be called when current event completes
		
		public static const NEXT_EVENT:String = "nextEvent";
		
		/**
		 * The position within the sequence
		 */
		public function get position():int {
			return _position;
		}
		
		/**
		 * The SequenceItem at the current
		 * position within the sequence.
		 */
		public function get currentItem():SequenceItem {
			return _currentItem;
		}
		
		/**
		 * Constructor 
		 */
		public function Sequence(autoStart:Boolean = true){
			addEventListener(NEXT_EVENT, nextEvent);
			if (autoStart) start(); // TODO: Make start a dispatch event rather than commands (w/ start handler)
		}
		
		/**
		 * Initiates a sequence using current position (not necessarily 0)
		 */
		public function start():void {
			nextItem = items[_position];
			initiateNextItem();
		}
		
		/**
		 * Stops a sequence from continuing but does not
		 * stop the current item from playing.
		 */
		public function stop():void {
			
			// remove listener if _currentItem exists
			if (_currentItem && _currentItem.target) {
				_currentItem.target.removeEventListener(_currentItem.eventType, itemComplete);
			}
		}
		
		/**
		 * Reset a sequence stopping it and setting its
		 * position to 0. If a current item is an instance
		 * of IStartable, it is stopped using stop()
		 */
		public function reset():void {
			
			// remove listener if _currentItem exists
			if (_currentItem && _currentItem.target) {
				_currentItem.target.removeEventListener(_currentItem.eventType, itemComplete);
				if (_currentItem.target is IStartable){
					IStartable(_currentItem.target).stop();
				}
			}
			
			// clear events and set _position to 0
			_currentItem = null;
			nextItem = null;
			_position = 0;
		}
		
		/**
		 * Resets sequence and removes all items 
		 */
		public function clear():void {
			reset();
			items.length = 0;
		}
		
		/**
		 * Adds an item in to the sequence.  This item will be started for
		 * its turn in the sequence and will need to signal its completion
		 * so that other items in the sequence will know when to begin.
		 * @param	target the target object to be played in the sequence.
		 * 			This will need to be an IEventDispatcher so it can 
		 * 			indicate it's completion to start the next itam
		 * 			in the sequence.
		 * @param	eventType The event type that sigifies the completion
		 * 			of the target item. By default this is Event.COMPLETE.
		 * @param	initiator The function that starts the item for the
		 * 			the sequence.  If the target is of the Gyro type
		 * 			IStartable, this can be ommitted and start() will
		 * 			be used.
		 * @param	initiatorArguments An array of arguments to be used in
		 * 			the intiator call.
		 */
		public function addItem(target:IEventDispatcher = null, eventType:String = Event.COMPLETE, initiator:Function = null, initiatorArguments:Array = null):SequenceItem {
			
			// assign default initiators if applicable
			if (initiator == null && target is IStartable) {
				initiator = IStartable(target).start;
			}
			
			// create new SequenceItem object to hold call, its
			// arguments and event information
			var addedItem:SequenceItem = new SequenceItem(target, eventType, initiator, initiatorArguments);
			
			// add event to items
			items.push(addedItem);
			
			// if added in the middle of a sequence and at the
			// end of that sequence, make this event the next event
			if (_currentItem && !nextItem) {
				nextItem = addedItem;
			}
			
			return addedItem;
		}
		
		/**
		 * Removes a SequenceItem instance from the sequence.
		 * SequenceItem instances are objects returned from
		 * addItem.
		 * @param	item The SequenceItem to remove from
		 * 			the sequence
		 * @return	The SequenceItem instance removed.
		 */
		public function removeItem(item:SequenceItem):SequenceItem {
			
			if (item == null) return null;
			
			// find the item in the list and
			// splice out if present
			var index:int = items.indexOf(item);
			if (index != -1){
				items.splice(index, 1);
			}
			
			return item;
		}
		
		/**
		 * Returns the target item at the specified index.
		 * @param	index The position number of the item to
		 * 			retrieve from the sequence list.
		 * @return	The target IEventDispatcher at the specified
		 * 			position in the sequence.  If the index is
		 * 			invalid, null is returned.
		 */
		private function getItemAt(index:int):IEventDispatcher {
			if (index >= 0 && index < items.length){
				return SequenceItem(items[index]).target;
			}
			return null;
		}
		
		/**
		 * Event handler reacting to completion of current sequence item
		 */
		private function itemComplete(event:Event):void {
			dispatchEvent(new Event(NEXT_EVENT));		
		}
		
		/**
		 * Event handler reacting to nextEvent event
		 */
		private function nextEvent(event:Event):void {
			initiateNextItem();			
		}
		
		/**
		 * Continues to the next sequence item after the completion of the current
		 */
		private function initiateNextItem():void {
			
			// remove the current listener
			if (_currentItem && _currentItem.target) {
				_currentItem.target.removeEventListener(_currentItem.eventType, itemComplete);
			}
			
			// if last item and sequence is complete, dispatch the complete event
			if (!nextItem) {
				dispatchEvent(new Event(Event.COMPLETE));
				return;
			}
			
			// reassign current item to next item
			_currentItem = nextItem;
			_currentItem.target.addEventListener(_currentItem.eventType, itemComplete);
			
			// update _position, if additional items exist
			// reassign nextItem to the next item, otherwise null
			_position++;
			nextItem = (_position < items.length) ? items[_position] : null;
			
			// intiiate the next item
			_currentItem.initiator.apply(_currentItem.target, _currentItem.initiatorArguments);
		}
		
		/**
		 * Event handler reacting to completion of sequence
		 */
		private function sequenceComplete(event:Event):void {
			
			// reset if no additional events exist
			if (!nextItem) {
				reset();
				return;
			}
			
			// if a nextItem exists, continue with that item
			initiateNextItem();
		}
	}
}