[Golist] [goasap commit] r60 - Fixes the initial commit so it works! I've now lightly tested the updates to SequenceCA and...

codesite-noreply at google.com codesite-noreply at google.com
Tue Mar 17 16:39:59 PDT 2009


Author: mosesoak
Date: Tue Mar 17 16:22:37 2009
New Revision: 60

Modified:
    branches/goasap0.5.2/src_go/org/goasap/utils/PlayableGroup.as
    branches/goasap0.5.2/src_go/org/goasap/utils/SequenceCA.as

Log:
Fixes the initial commit so it works! I've now lightly tested the updates  
to SequenceCA and the new ImmediateAdvance class, both of which seem to  
work.

Modified: branches/goasap0.5.2/src_go/org/goasap/utils/PlayableGroup.as
==============================================================================
--- branches/goasap0.5.2/src_go/org/goasap/utils/PlayableGroup.as	(original)
+++ branches/goasap0.5.2/src_go/org/goasap/utils/PlayableGroup.as	Tue Mar  
17 16:22:37 2009
@@ -1 +1 @@
-/**
  * Copyright (c) 2007 Moses Gunesch
  *
  * Permission is hereby granted, free of charge, to any person obtaining a  
copy
  * of this software and associated documentation files (the "Software"), to  
deal
  * in the Software without restriction, including without limitation the  
rights
  * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  * copies of the Software, and to permit persons to whom the Software is
  * furnished to do so, subject to the following conditions:
  *
  * The above copyright notice and this permission notice shall be included  
in
  * all copies or substantial portions of the Software.
  *
  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS  
OR
  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL  
THE
  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING  
FROM,
  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
  * THE SOFTWARE.
  */
package org.goasap.utils {
	import org.goasap.PlayStates;
	import org.goasap.PlayableBase;
	import org.goasap.events.GoEvent;
	import org.goasap.interfaces.IPlayable;
	import org.goasap.managers.Repeater;
	
	import flash.utils.Dictionary;	

	/**
	 * Dispatched when the group starts.
	 * @eventType org.goasap.events.START
	 */
	[Event(name="START", type="org.goasap.events.GoEvent")]

	/**
	 * Dispatched when the group is paused successfully.
	 * @eventType org.goasap.events.PAUSE
	 */
	[Event(name="PAUSE", type="org.goasap.events.GoEvent")]

	/**
	 * Dispatched when the group is resumed successfully.
	 * @eventType org.goasap.events.RESUME
	 */
	[Event(name="RESUME", type="org.goasap.events.GoEvent")]

	/**
	 * Dispatched at the end the group if <code>repeater.cycles</code> is set  
to
	 * a value other than one, just before the group starts its next play  
cycle.
	 * @eventType org.goasap.events.CYCLE
	 */
	[Event(name="CYCLE", type="org.goasap.events.GoEvent")]

	/**
	 * Dispatched if the group is manually stopped.
	 * @eventType org.goasap.events.STOP
	 */
	[Event(name="STOP", type="org.goasap.events.GoEvent")]

	/**
	 * Dispatched after all children have dispatched a STOP or COMPLETE event.
	 * @eventType org.goasap.events.COMPLETE
	 */
	[Event(name="COMPLETE", type="org.goasap.events.GoEvent")]


	/**
	 * Batch-play a set of items and receive an event when all of them have  
finished.
	 *
	 * <p>PlayableGroup accepts any IPlayable for its children, which can  
include
	 * tweens, other groups, sequences and so forth. The group listens for both
	 * GoEvent.STOP and GoEvent.COMPLETE events from its children, either of  
which
	 * are counted toward group completion.</p>
	 *
	 * <p>The <code>repeater</code> property of PlayableGroup allows you to  
loop play
	 * any number of times, or indefinitely by setting its cycles to  
Repeater.INFINITE.
	 * GoEvent.CYCLE is dispatched on each loop and GoEvent.COMPLETE when  
finished.
	 * Other events dispatched include the GoEvent types START, STOP, PAUSE,  
and RESUME.</p>
	 *
	 * @author Moses Gunesch
	 */
	public class PlayableGroup extends PlayableBase implements IPlayable {
		
		// -== Public Properties ==-
		
		/**
		 * Get or set the children array. Only IPlayable items are stored. Note  
that
		 * unlike the methods <code>addChild</code> and <code>removeChild</code>,
		 * setting this property will stop any group play currently in progress.
		 */
		public function get children():Array {
			var a:Array = [];
			for (var item:Object in _children)
				a.push(item);
			return a;
		}
		public function set children(a:Array):void {
			if (_listeners > 0)
				stop();
			for each (var item:Object in a)
				if (item is IPlayable)
					addChild(item as IPlayable);
		}
		
		/**
		 * The groups's Repeater instance, which may be used to make
		 * it loop and play more than one time.
		 *
		 * <p>The Repeater's cycles property can be set to an integer, or
		 * to Repeater.INFINITE or 0 to repeat indefinitely.</p>
		 *
		 * <pre>
		 * var group:PlayableGroup = new PlayableGroup(tween1, tween2, tween3);
		 * group.repeater.cycles = 2;
		 * group.start();
		 * trace(group.repeater.currentCycle); // output: 0
		 * </pre>
		 */
		public function get repeater(): Repeater {
			return _repeater;
		}
		
		/**
		 * Determines the number of children currently being monitored
		 * for completion by the group.
		 */
		public function get listenerCount() : uint {
			return _listeners;
		}
		
		// -== Protected Properties ==-
		
		/** @private */
		protected var _children: Dictionary = new Dictionary();
		
		/** @private */
		protected var _listeners: uint = 0;
		
		/** @private */
		protected var _repeater: Repeater;
		
		// -== Public Methods ==-
		
		/**
		 * Constructor.
		 *
		 * @param items	Any number of IPlayable items as separate arguments,
		 * 					or a single array of them.
		 */
		public function PlayableGroup(...items) {
			super();
			if (items.length > 0)
				this.children = ((items[ 0 ] is Array) ? items[ 0 ] : items);
			_repeater = new Repeater();
			_repeater.setParent(this);
		}
		
		/**
		 * Searches for a child with the specified playableID.
		 *
		 * @param playableID	The item playableID to search for. (The item must  
have a
		 * 						property called <code>playableID</code> which is a general
		 * 						GoASAP convention established in PlayableBase.)
		 * @param deepSearch	If child is not found in the group, this option runs  
a
		 * 						recursive search on any children that are PlayableGroup.
		 * @return				The SequenceStep with the matching playableID.
		 */
		public function getChildByID(playableID:*,  
deepSearch:Boolean=true):IPlayable {
			for (var item:Object in _children)
				if (item.hasOwnProperty("playableID") &&  
item["playableID"]===playableID)
					return (item as IPlayable);
			if (deepSearch) {
				for (item in _children) {
					if (item is PlayableGroup) {
						var match:IPlayable = ((item as  
PlayableGroup).getChildByID(playableID, true));
						if (match) { return (match as IPlayable); }
					}
				}
			}
			return null;
		}
		
		/**
		 * Adds a single IPlayable to the children array (duplicates are  
rejected) and
		 * syncs up the group and child play-states based on various conditions.
		 *
		 * <p>Normally this method is called when both the group and the child  
item are
		 * not playing. However in some cases either the group or the child might  
be
		 * playing or paused, in which case calling this method will attempt to  
sync
		 * the child state with the group's state. If syncing fails the child is  
not added
		 * and false is returned.</p>
		 *
		 * @param item				Any instance that implements IPlayable and uses  
PlayState constants.
		 * @return 					The item added, or null if the item was already in the  
group or
		 * 							a mismatched child state was not syncable to the group's state.
		 */
		public function addChild(item:IPlayable): IPlayable {
			var itemState:String = item.state;
			if (_children[ item ]!=null) {
				return null; // child already added!
			}
			_children[ item ] = false;
			if (_state==PlayStates.STOPPED && itemState!=PlayStates.STOPPED) {
				item.stop();
			}
			if (itemState==_state) {
				return item; // states match: no further action is needed. this is most  
common.
			}
			
			// Group is active: attempt syncing of child state to group state.
			var success:Boolean = true;
			listenTo(item);
			if (_state==PlayStates.PLAYING || _state==PlayStates.PLAYING_DELAY) {
				if (itemState==PlayStates.PAUSED) {
					success = item.resume();
				}
				else if (itemState==PlayStates.STOPPED) {
					success = item.start();
				}
				else {
					return item; // Item is already playing and only delay states  
mismatch: Success.
				}
			}
			else if (_state==PlayStates.PAUSED) {
				if (itemState==PlayStates.STOPPED) {
					item.start();
				}
				success = item.pause();
			}
			if (!success) {
				unListenTo(item);
				delete _children[ item ];
				return null;
			}
			return item;
		}
		
		/**
		 * Removes a single IPlayable from the children array.
		 *
		 * <p>Note that if play is in progress when a child is added it does not
		 * interrupt play and the child is monitored for completion along with
		 * others.</p>
		 *
		 * @param item		Any instance that implements IPlayable and uses PlayState  
constants.
		 * @return 			The item removed, or null if the call failed.
		 */
		public function removeChild(item:IPlayable): IPlayable {
			var v:* = _children[ item ];
			if (v===null)
				return null;
			if (v===true)
				unListenTo( item );
			delete _children[ item ];
			return item;
		}
		
		/**
		 * Enables already-playing items to be added to a group seamlessly by  
presetting
		 * the group's state to PLAYING instead of interrupting the child item.
		 *
		 * @param item		Any instance that implements IPlayable and uses PlayState  
constants.
		 * @return 			The item added, or null if the call failed.
		 */
		public function addPlayingChildAndStart(item:IPlayable): IPlayable {
			_state = PlayStates.PLAYING;
			item = addChild(item); // Item will start if stopped and resume if  
paused.
			if (_listeners > 0) {
				dispatchEvent(new GoEvent( GoEvent.START));
				_playRetainer[ this ] = 1; // Developers - Important! Look up  
_playRetainer.
			}
			else {
				_state = PlayStates.STOPPED;
			}
			return item;
		}
		
		/**
		 * Test whether any child has a particular PlayState.
		 *
		 * <pre>
		 * // Example: resume a paused group
		 * if ( myGroup.anyChildHasState(PlayStates.PlayStates.PAUSED) ) {
		 *     myGroup.resume();
		 * }
		 * </pre>
		 * @see org.goasap.PlayStates PlayStates
		 */
		public function anyChildHasState(state:String): Boolean {
			for (var item:Object in _children)
				if ((item as IPlayable).state==state)
					return true;
			return false;
		}
		
		// -== IPlayable implementation ==-
		
		/**
		 * Calls start on all children.
		 *
		 * <p>If the group is active when this method is called, a  
<code>stop</code> call
		 * is automated which will result in a GoEVent.STOP event being  
dispatched.</p>
		 *
		 * @return Returns true if any child in the group starts successfully.
		 */
		public function start() : Boolean {
			stop();
			var r:Boolean = false;
			var c:Array = this.children; // array is used because cycling Dictionary  
and setting entries can result in duplicates (Dictionary bug, apparently.)
			for each (var item:IPlayable in c) {
				listenTo(item); // first in case of immediate STOP/COMPLETE.
				var started:Boolean = item.start();
				if (!started) {
					unListenTo(item);
				}
				r = (started || r);
			}
			if (!r) return false; // all starts failed
			_state = PlayStates.PLAYING;
			dispatchEvent(new GoEvent( GoEvent.START));
			_playRetainer[ this ] = 1; // Developers - Important! Look up  
_playRetainer.
			return true;
		}

		/**
		 * If the group is active, this method stops all child items and
		 * dispatches a GoEvent.STOP event.
		 *
		 * @return Returns true only if all children in the group stop  
successfully.
		 */
		public function stop() : Boolean {
			if (_state == PlayStates.STOPPED)
				return false;
			_state = PlayStates.STOPPED;
			_repeater.reset();
			delete _playRetainer[ this ]; // Developers - Important! Look up  
_playRetainer.
			if (_listeners==0) {
				dispatchEvent(new GoEvent( GoEvent.COMPLETE ));
				return true;
			}
			var r:Boolean = true;
			var c:Array = this.children; // array is used because cycling Dictionary  
and setting entries can result in duplicates (Dictionary bug, apparently.)
			for each (var item:IPlayable in c) {
				unListenTo(item);
				r = (item.stop() && r);
			}
			dispatchEvent(new GoEvent( GoEvent.STOP ));
			return r;
		}
		
		/**
		 * Calls <code>pause</code> on all children.
		 *
		 * @return  Returns true only if all playing children in the group paused  
successfully
		 * 			and at least one child was paused.
		 */
		public function pause() : Boolean {
			if (_state!= PlayStates.PLAYING)
				return false;
			var r:Boolean = true;
			var n:uint = 0;
			var c:Array = this.children; // array is used because cycling Dictionary  
and setting entries can result in duplicates (Dictionary bug, apparently.)
			for each (var item:IPlayable in c) {
				var success:Boolean = item.pause();
				if (success) n++;
				r = (r && success);
			}
			if (n>0) {
				_state = PlayStates.PAUSED; // state should reflect that at least one  
item was paused,
								  // while return value may indicate that not all pause calls  
succeeded.
				dispatchEvent(new GoEvent( GoEvent.PAUSE ));
			}
			return (n>0 && r);
		}
		
		/**
		 * Calls <code>resume</code> on all children.
		 *
		 * @return	Returns true only if all paused children in the group resumed  
successfully
		 * 			and at least one child was resumed.
		 */
		public function resume() : Boolean {
			if (_state!= PlayStates.PAUSED)
				return false;
			var r:Boolean = true;
			var n:uint = 0;
			var c:Array = this.children; // array is used because cycling Dictionary  
and setting entries can result in duplicates (Dictionary bug, apparently.)
			for each (var item:IPlayable in c) {
				var success:Boolean = item.resume();
				if (success) n++;
				r = (r && success);
			}
			if (n>0) {
				_state = PlayStates.PLAYING; // state should reflect that at least one  
item was resumed,
								  // while return value may indicate that not all resume calls  
succeeded.
				dispatchEvent(new GoEvent( GoEvent.RESUME ));
			}
			return (n>0 && r);
		}
		
		/**
		 * Calls <code>skipTo</code> on all children.
		 *
		 * @return	Returns true only if all children in the group skipTo the  
position successfully
		 * 			and at least one child was affected.
		 */
		public function skipTo(position : Number) : Boolean {
			var r:Boolean = true;
			var n:uint = 0;
			position = _repeater.skipTo(_repeater.cycles, position); // TODO: TEST
			var c:Array = this.children; // array is used because cycling Dictionary  
and setting entries can result in duplicates (Dictionary bug, apparently.)
			for each (var item:IPlayable in c) {
				listenTo(item); // first in case of immediate STOP/COMPLETE.
				var started:Boolean = item.skipTo(position);
				if (!started || item.state==PlayStates.STOPPED) {
					unListenTo(item);
					started = false;
				}
				else { n++; }
				r = (started && r);
			}
			_state = (r ? PlayStates.PLAYING : PlayStates.STOPPED);
			return (n>0 && r);
		}
		
		// -== Protected Methods ==-
		
		/**
		 * @private
		 * Internal handler for item completion.
		 * @param event		GoEvent dispatched by child item.
		 */
		protected function onItemEnd(event:GoEvent) : void {
			unListenTo(event.target as IPlayable);
			if (_listeners==0) {
				complete();
			}
		}
		
		/**
		 * @private
		 * Internal handler for group completion.
		 */
		protected function complete() : void {
			if (_repeater.next()) {
				dispatchEvent(new GoEvent( GoEvent.CYCLE ));
				var c:Array = this.children; // array is used because cycling  
Dictionary and setting entries can result in duplicates (Dictionary bug,  
apparently.)
				for each (var item:IPlayable in c) {
					listenTo(item); // first in case of immediate STOP/COMPLETE.
					var started:Boolean = (item).start();
					if (!started || item.state==PlayStates.STOPPED)
						unListenTo(item);
				}
			}
			else {
				stop();
			}
		}

		/**
		 * @private
		 * Internal. Listen for item completion, keeping tight track of listeners.
		 * @param item	Any instance that extends IPlayable (IPlayable itself  
should not be used directly).
		 */
		protected function listenTo(item:IPlayable) : void {
			if (_children[ item ] === false) {
				item.addEventListener(GoEvent.STOP, onItemEnd, false, 0, true);
				item.addEventListener(GoEvent.COMPLETE, onItemEnd, false, 0, true);
				_children[ item ] = true;
				_listeners++;
			}
		}
		
		/**
		 * @private
		 * Internal. Stop listening for item completion.
		 * @param item	Any instance that extends IPlayable (IPlayable itself  
should not be used directly).
		 * @return Number of completion listeners remaining.
		 */
		protected function unListenTo(item:IPlayable) : void {
			if (_children[ item ] === true) {
				item.removeEventListener(GoEvent.STOP, onItemEnd);
				item.removeEventListener(GoEvent.COMPLETE, onItemEnd);
				_children[ item ] = false;
				_listeners--;
			}
		}
	}
}
\ No newline at end of file
+/**
  * Copyright (c) 2007 Moses Gunesch
  *
  * Permission is hereby granted, free of charge, to any person obtaining a  
copy
  * of this software and associated documentation files (the "Software"), to  
deal
  * in the Software without restriction, including without limitation the  
rights
  * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  * copies of the Software, and to permit persons to whom the Software is
  * furnished to do so, subject to the following conditions:
  *
  * The above copyright notice and this permission notice shall be included  
in
  * all copies or substantial portions of the Software.
  *
  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS  
OR
  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL  
THE
  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING  
FROM,
  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
  * THE SOFTWARE.
  */
package org.goasap.utils {
	import org.goasap.PlayStates;
	import org.goasap.PlayableBase;
	import org.goasap.events.GoEvent;
	import org.goasap.interfaces.IPlayable;
	import org.goasap.managers.Repeater;
	
	import flash.utils.Dictionary;	

	/**
	 * Dispatched when the group starts.
	 * @eventType org.goasap.events.START
	 */
	[Event(name="START", type="org.goasap.events.GoEvent")]

	/**
	 * Dispatched when the group is paused successfully.
	 * @eventType org.goasap.events.PAUSE
	 */
	[Event(name="PAUSE", type="org.goasap.events.GoEvent")]

	/**
	 * Dispatched when the group is resumed successfully.
	 * @eventType org.goasap.events.RESUME
	 */
	[Event(name="RESUME", type="org.goasap.events.GoEvent")]

	/**
	 * Dispatched at the end the group if <code>repeater.cycles</code> is set  
to
	 * a value other than one, just before the group starts its next play  
cycle.
	 * @eventType org.goasap.events.CYCLE
	 */
	[Event(name="CYCLE", type="org.goasap.events.GoEvent")]

	/**
	 * Dispatched if the group is manually stopped.
	 * @eventType org.goasap.events.STOP
	 */
	[Event(name="STOP", type="org.goasap.events.GoEvent")]

	/**
	 * Dispatched after all children have dispatched a STOP or COMPLETE event.
	 * @eventType org.goasap.events.COMPLETE
	 */
	[Event(name="COMPLETE", type="org.goasap.events.GoEvent")]


	/**
	 * Batch-play a set of items and receive an event when all of them have  
finished.
	 *
	 * <p>PlayableGroup accepts any IPlayable for its children, which can  
include
	 * tweens, other groups, sequences and so forth. The group listens for both
	 * GoEvent.STOP and GoEvent.COMPLETE events from its children, either of  
which
	 * are counted toward group completion.</p>
	 *
	 * <p>The <code>repeater</code> property of PlayableGroup allows you to  
loop play
	 * any number of times, or indefinitely by setting its cycles to  
Repeater.INFINITE.
	 * GoEvent.CYCLE is dispatched on each loop and GoEvent.COMPLETE when  
finished.
	 * Other events dispatched include the GoEvent types START, STOP, PAUSE,  
and RESUME.</p>
	 *
	 * @author Moses Gunesch
	 */
	public class PlayableGroup extends PlayableBase implements IPlayable {
		
		// -== Public Properties ==-
		
		/**
		 * Get or set the children array. Only IPlayable items are stored. Note  
that
		 * unlike the methods <code>addChild</code> and <code>removeChild</code>,
		 * setting this property will stop any group play currently in progress.
		 */
		public function get children():Array {
			var a:Array = [];
			for (var item:Object in _children)
				a.push(item);
			return a;
		}
		public function set children(a:Array):void {
			if (_listeners > 0)
				stop();
			for each (var item:Object in a)
				if (item is IPlayable)
					addChild(item as IPlayable);
		}
		
		/**
		 * The groups's Repeater instance, which may be used to make
		 * it loop and play more than one time.
		 *
		 * <p>The Repeater's cycles property can be set to an integer, or
		 * to Repeater.INFINITE or 0 to repeat indefinitely.</p>
		 *
		 * <pre>
		 * var group:PlayableGroup = new PlayableGroup(tween1, tween2, tween3);
		 * group.repeater.cycles = 2;
		 * group.start();
		 * trace(group.repeater.currentCycle); // output: 0
		 * </pre>
		 */
		public function get repeater(): Repeater {
			return _repeater;
		}
		
		/**
		 * Determines the number of children currently being monitored
		 * for completion by the group.
		 */
		public function get listenerCount() : uint {
			return _listeners;
		}
		
		// -== Protected Properties ==-
		
		/** @private */
		protected var _children: Dictionary = new Dictionary();
		
		/** @private */
		protected var _listeners: uint = 0;
		
		/** @private */
		protected var _repeater: Repeater;
		
		// -== Public Methods ==-
		
		/**
		 * Constructor.
		 *
		 * @param items	Any number of IPlayable items as separate arguments,
		 * 					or a single array of them.
		 */
		public function PlayableGroup(...items) {
			super();
			if (items.length > 0)
				this.children = ((items[ 0 ] is Array) ? items[ 0 ] : items);
			_repeater = new Repeater();
			_repeater.setParent(this);
		}
		
		/**
		 * Searches for a child with the specified playableID.
		 *
		 * @param playableID	The item playableID to search for. (The item must  
have a
		 * 						property called <code>playableID</code> which is a general
		 * 						GoASAP convention established in PlayableBase.)
		 * @param deepSearch	If child is not found in the group, this option runs  
a
		 * 						recursive search on any children that are PlayableGroup.
		 * @return				The SequenceStep with the matching playableID.
		 */
		public function getChildByID(playableID:*,  
deepSearch:Boolean=true):IPlayable {
			for (var item:Object in _children)
				if (item.hasOwnProperty("playableID") &&  
item["playableID"]===playableID)
					return (item as IPlayable);
			if (deepSearch) {
				for (item in _children) {
					if (item is PlayableGroup) {
						var match:IPlayable = ((item as  
PlayableGroup).getChildByID(playableID, true));
						if (match) { return (match as IPlayable); }
					}
				}
			}
			return null;
		}
		
		/**
		 * Adds a single IPlayable to the children array (duplicates are  
rejected) and
		 * syncs up the group and child play-states based on various conditions.
		 *
		 * <p>Normally this method is called when both the group and the child  
item are
		 * not playing. However in some cases either the group or the child might  
be
		 * playing or paused, in which case calling this method will attempt to  
sync
		 * the child state with the group's state. If syncing fails the child is  
not added
		 * and false is returned.</p>
		 *
		 * @param item				Any instance that implements IPlayable and uses  
PlayState constants.
		 * @return 					The item added, or null if the item was already in the  
group or
		 * 							a mismatched child state was not syncable to the group's state.
		 */
		public function addChild(item:IPlayable): IPlayable {
			var itemState:String = item.state;
			if (_children[ item ]!=null) {
				return null; // child already added!
			}
			_children[ item ] = false;
			if (_state==PlayStates.STOPPED) { // group is inactive, sync item by  
stopping if necessary.
				if (itemState!=PlayStates.STOPPED) {
					item.stop();
				}
				return item;
			}
			
			// Group is active: attempt syncing of child state to group state.
			var success:Boolean = true;
			listenTo(item);
			if (_state==PlayStates.PLAYING || _state==PlayStates.PLAYING_DELAY) {
				if (itemState==PlayStates.PAUSED) {
					success = item.resume();
				}
				else if (itemState==PlayStates.STOPPED) {
					success = item.start();
				}
				else {
					return item; // Item is already playing and only delay states  
mismatch: Success.
				}
			}
			else if (_state==PlayStates.PAUSED) {
				if (itemState==PlayStates.STOPPED) {
					item.start();
				}
				success = item.pause();
			}
			if (!success) {
				unListenTo(item);
				delete _children[ item ];
				return null;
			}
			return item;
		}
		
		/**
		 * Removes a single IPlayable from the children array.
		 *
		 * <p>Note that if play is in progress when a child is added it does not
		 * interrupt play and the child is monitored for completion along with
		 * others.</p>
		 *
		 * @param item		Any instance that implements IPlayable and uses PlayState  
constants.
		 * @return 			The item removed, or null if the call failed.
		 */
		public function removeChild(item:IPlayable): IPlayable {
			var v:* = _children[ item ];
			if (v===null)
				return null;
			if (v===true)
				unListenTo( item );
			delete _children[ item ];
			return item;
		}
		
		/**
		 * Enables already-playing items to be added to a group seamlessly by  
presetting
		 * the group's state to PLAYING instead of interrupting the child item.
		 *
		 * @param item		Any instance that implements IPlayable and uses PlayState  
constants.
		 * @return 			The item added, or null if the call failed.
		 */
		public function addPlayingChildAndStart(item:IPlayable): IPlayable {
			_state = PlayStates.PLAYING;
			item = addChild(item); // Item will start if stopped and resume if  
paused.
			if (_listeners > 0) {
				dispatchEvent(new GoEvent( GoEvent.START));
				_playRetainer[ this ] = 1; // Developers - Important! Look up  
_playRetainer.
			}
			else {
				_state = PlayStates.STOPPED;
			}
			return item;
		}
		
		/**
		 * Test whether any child has a particular PlayState.
		 *
		 * <pre>
		 * // Example: resume a paused group
		 * if ( myGroup.anyChildHasState(PlayStates.PlayStates.PAUSED) ) {
		 *     myGroup.resume();
		 * }
		 * </pre>
		 * @see org.goasap.PlayStates PlayStates
		 */
		public function anyChildHasState(state:String): Boolean {
			for (var item:Object in _children)
				if ((item as IPlayable).state==state)
					return true;
			return false;
		}
		
		// -== IPlayable implementation ==-
		
		/**
		 * Calls start on all children.
		 *
		 * <p>If the group is active when this method is called, a  
<code>stop</code> call
		 * is automated which will result in a GoEVent.STOP event being  
dispatched.</p>
		 *
		 * @return Returns true if any child in the group starts successfully.
		 */
		public function start() : Boolean {
			stop();
			var r:Boolean = false;
			var c:Array = this.children; // array is used because cycling Dictionary  
and setting entries can result in duplicates (Dictionary bug, apparently.)
			for each (var item:IPlayable in c) {
				listenTo(item); // first in case of immediate STOP/COMPLETE.
				var started:Boolean = item.start();
				if (!started) {
					unListenTo(item);
				}
				r = (started || r);
			}
			if (!r) return false; // all starts failed
			_state = PlayStates.PLAYING;
			dispatchEvent(new GoEvent( GoEvent.START));
			_playRetainer[ this ] = 1; // Developers - Important! Look up  
_playRetainer.
			return true;
		}

		/**
		 * If the group is active, this method stops all child items and
		 * dispatches a GoEvent.STOP event.
		 *
		 * @return Returns true only if all children in the group stop  
successfully.
		 */
		public function stop() : Boolean {
			if (_state == PlayStates.STOPPED)
				return false;
			_state = PlayStates.STOPPED;
			_repeater.reset();
			delete _playRetainer[ this ]; // Developers - Important! Look up  
_playRetainer.
			if (_listeners==0) {
				dispatchEvent(new GoEvent( GoEvent.COMPLETE ));
				return true;
			}
			var r:Boolean = true;
			var c:Array = this.children; // array is used because cycling Dictionary  
and setting entries can result in duplicates (Dictionary bug, apparently.)
			for each (var item:IPlayable in c) {
				unListenTo(item);
				r = (item.stop() && r);
			}
			dispatchEvent(new GoEvent( GoEvent.STOP ));
			return r;
		}
		
		/**
		 * Calls <code>pause</code> on all children.
		 *
		 * @return  Returns true only if all playing children in the group paused  
successfully
		 * 			and at least one child was paused.
		 */
		public function pause() : Boolean {
			if (_state!= PlayStates.PLAYING)
				return false;
			var r:Boolean = true;
			var n:uint = 0;
			var c:Array = this.children; // array is used because cycling Dictionary  
and setting entries can result in duplicates (Dictionary bug, apparently.)
			for each (var item:IPlayable in c) {
				var success:Boolean = item.pause();
				if (success) n++;
				r = (r && success);
			}
			if (n>0) {
				_state = PlayStates.PAUSED; // state should reflect that at least one  
item was paused,
								  // while return value may indicate that not all pause calls  
succeeded.
				dispatchEvent(new GoEvent( GoEvent.PAUSE ));
			}
			return (n>0 && r);
		}
		
		/**
		 * Calls <code>resume</code> on all children.
		 *
		 * @return	Returns true only if all paused children in the group resumed  
successfully
		 * 			and at least one child was resumed.
		 */
		public function resume() : Boolean {
			if (_state!= PlayStates.PAUSED)
				return false;
			var r:Boolean = true;
			var n:uint = 0;
			var c:Array = this.children; // array is used because cycling Dictionary  
and setting entries can result in duplicates (Dictionary bug, apparently.)
			for each (var item:IPlayable in c) {
				var success:Boolean = item.resume();
				if (success) n++;
				r = (r && success);
			}
			if (n>0) {
				_state = PlayStates.PLAYING; // state should reflect that at least one  
item was resumed,
								  // while return value may indicate that not all resume calls  
succeeded.
				dispatchEvent(new GoEvent( GoEvent.RESUME ));
			}
			return (n>0 && r);
		}
		
		/**
		 * Calls <code>skipTo</code> on all children.
		 *
		 * @return	Returns true only if all children in the group skipTo the  
position successfully
		 * 			and at least one child was affected.
		 */
		public function skipTo(position : Number) : Boolean {
			var r:Boolean = true;
			var n:uint = 0;
			position = _repeater.skipTo(_repeater.cycles, position); // TODO: TEST
			var c:Array = this.children; // array is used because cycling Dictionary  
and setting entries can result in duplicates (Dictionary bug, apparently.)
			for each (var item:IPlayable in c) {
				listenTo(item); // first in case of immediate STOP/COMPLETE.
				var started:Boolean = item.skipTo(position);
				if (!started || item.state==PlayStates.STOPPED) {
					unListenTo(item);
					started = false;
				}
				else { n++; }
				r = (started && r);
			}
			_state = (r ? PlayStates.PLAYING : PlayStates.STOPPED);
			return (n>0 && r);
		}
		
		// -== Protected Methods ==-
		
		/**
		 * @private
		 * Internal handler for item completion.
		 * @param event		GoEvent dispatched by child item.
		 */
		protected function onItemEnd(event:GoEvent) : void {
			unListenTo(event.target as IPlayable);
			if (_listeners==0) {
				complete();
			}
		}
		
		/**
		 * @private
		 * Internal handler for group completion.
		 */
		protected function complete() : void {
			if (_repeater.next()) {
				dispatchEvent(new GoEvent( GoEvent.CYCLE ));
				var c:Array = this.children; // array is used because cycling  
Dictionary and setting entries can result in duplicates (Dictionary bug,  
apparently.)
				for each (var item:IPlayable in c) {
					listenTo(item); // first in case of immediate STOP/COMPLETE.
					var started:Boolean = (item).start();
					if (!started || item.state==PlayStates.STOPPED)
						unListenTo(item);
				}
			}
			else {
				stop();
			}
		}

		/**
		 * @private
		 * Internal. Listen for item completion, keeping tight track of listeners.
		 * @param item	Any instance that extends IPlayable (IPlayable itself  
should not be used directly).
		 */
		protected function listenTo(item:IPlayable) : void {
			if (_children[ item ] === false) {
				item.addEventListener(GoEvent.STOP, onItemEnd, false, 0, true);
				item.addEventListener(GoEvent.COMPLETE, onItemEnd, false, 0, true);
				_children[ item ] = true;
				_listeners++;
			}
		}
		
		/**
		 * @private
		 * Internal. Stop listening for item completion.
		 * @param item	Any instance that extends IPlayable (IPlayable itself  
should not be used directly).
		 * @return Number of completion listeners remaining.
		 */
		protected function unListenTo(item:IPlayable) : void {
			if (_children[ item ] === true) {
				item.removeEventListener(GoEvent.STOP, onItemEnd);
				item.removeEventListener(GoEvent.COMPLETE, onItemEnd);
				_children[ item ] = false;
				_listeners--;
			}
		}
	}
}
\ No newline at end of file

Modified: branches/goasap0.5.2/src_go/org/goasap/utils/SequenceCA.as
==============================================================================
--- branches/goasap0.5.2/src_go/org/goasap/utils/SequenceCA.as	(original)
+++ branches/goasap0.5.2/src_go/org/goasap/utils/SequenceCA.as	Tue Mar 17  
16:22:37 2009
@@ -1 +1 @@
-/**
  * Copyright (c) 2007 Moses Gunesch
  *
  * Permission is hereby granted, free of charge, to any person obtaining a  
copy
  * of this software and associated documentation files (the "Software"), to  
deal
  * in the Software without restriction, including without limitation the  
rights
  * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  * copies of the Software, and to permit persons to whom the Software is
  * furnished to do so, subject to the following conditions:
  *
  * The above copyright notice and this permission notice shall be included  
in
  * all copies or substantial portions of the Software.
  *
  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS  
OR
  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL  
THE
  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING  
FROM,
  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
  * THE SOFTWARE.
  */
package org.goasap.utils {
	import org.goasap.PlayStates;
	import org.goasap.events.GoEvent;
	import org.goasap.events.SequenceEvent;
	import org.goasap.interfaces.IPlayable;
	
	import flash.events.Event;	

	/**
	 * Sequence with "Custom Advance" options, in which steps can specify when  
they should advance.
	 *
	 * <p>This class works like Sequence but uses the special class  
SequenceStepCA for its steps.
	 * SequenceStepCA has a property called <code>advance</code>. When steps  
advance before animation
	 * finishes, the trailing steps are tracked so that the SequenceCA doesn't  
dispatch its COMPLETE
	 * event until all activity has completed.</p>
	 *
	 * <p>Any step's advance property can be set to an instance of  
OnDurationComplete, OnPlayableComplete,
	 * OnEventComplete or OnConditionTrue. Each of those classes defines its  
own parameters and rules for
	 * when the advance occurs. For example, using OnPlayableComplete a  
sequence can advance after one
	 * particular item in the step finishes, without needing to wait for all  
the other ones in that group
	 * to complete.</p>
	 *
	 * <p>Additionally, you can create your own custom advance types by  
subclassing the SequenceAdvance
	 * base class.</p>
	 *
	 * @see SequenceStepCA
	 * @see org.goasap.utils.customadvance.OnConditionTrue OnConditionTrue
	 * @see org.goasap.utils.customadvance.OnDurationComplete  
OnDurationComplete
	 * @see org.goasap.utils.customadvance.OnEventComplete OnEventComplete
	 * @see org.goasap.utils.customadvance.OnPlayableComplete  
OnPlayableComplete
	 * @see org.goasap.utils.customadvance.SequenceAdvance SequenceAdvance
	 * @see Sequence
	 * @see SequenceBase
	 *
	 * @author Moses Gunesch
	 */
	public class SequenceCA extends SequenceBase {
		
		// -== Public Properties ==-
		
		// Also in super:
		// length : uint   [Read-only.]
		// playIndex : int [Read-only.]
		// steps : Array
		// start() : Boolean
		// stop() : Boolean
		// pause() : Boolean
		// resume() : Boolean
		// skipTo(index:Number) : Boolean
		
		/**
		 * Returns the currently-playing SequenceStepCA.
		 * @return The currently-playing SequenceStepCA.
		 * @see #getStepAt()
		 * @see #getStepByID()
		 * @see #steps
		 * @see #lastStep
		 */
		public function get currentStep() : SequenceStepCA {
			return (super._getCurrentStep() as SequenceStepCA);
		}
		
		/**
		 * Returns the final SequenceStepCA in the current sequence.
		 * @return The final SequenceStepCA in the current sequence.
		 * @see #getStepAt()
		 * @see #getStepByID()
		 * @see #steps
		 * @see #currentStep
		 */
		public function get lastStep() : SequenceStepCA {
			return (super._getLastStep() as SequenceStepCA);
		}
		
		// -== Protected Properties ==-
		
		/**
		 * @private
		 */
		protected var _trailingSteps : SequenceStep;
		
		// -== Public Methods ==-
		
		/**
		 * Constructor.
		 *
		 * @param items		Any number of IPlayable instances (e.g. LinearGo,  
PlayableGroup,
		 * 					SequenceStepCA) as separate arguments, or a single array of them.
		 */
		public function SequenceCA(...items) {
			super((items[ 0 ] is Array) ? items[ 0 ] : items);
		}
		
		/**
		 * Retrieves any SequenceStepCA from the steps array.
		 * @param index		An array index starting at 0.
		 * @return			The SequenceStepCA instance at this index.
		 * @see #getStepByID()
		 */
		public function getStepAt(index:int) : SequenceStepCA {
			return (super._getStepAt(index) as SequenceStepCA);
		}
		
		/**
		 * Locates a step with the specified playableID. To search within a step  
for a
		 * child by playableID, use the step instance's <code>getChildByID</code>  
method.
		 *
		 * @param playableID	The step instance's playableID to search for.
		 * @return				The SequenceStepCA with the matching playableID.
		 */
		public function getStepByID(playableID:*) : SequenceStepCA {
			return (super._getStepByID(playableID) as SequenceStepCA);
		}
		
		/**
		 * Adds a single SequenceStepCA or IPlayable instance (LinearGo,  
PlayableGroup,
		 * etc.) to the end of the steps array, or optionally adds the instance  
into the
		 * last SequenceStepCA instead of adding it as a new step. Calling this
		 * method stops any sequence play currently in progress.
		 *
		 * <p>To remove a step use the <code>removeStepAt</code> method.</p>
		 *
		 * @param item			The playable item to add to the sequence. Note
		 * 						that when new steps are added, any IPlayable
		 * 						instance of a type other than SequenceStepCA is
		 * 						automatically wrapped in a new SequenceStepCA.
		 * 						
		 * @param addToLastStep	If true is passed the item is added to the last
		 * 						existing SequenceStepCA in the steps array. This
		 * 						option should be used with individual items that
		 * 						you want added as children to the SequenceStepCA.
		 * 						If there are no steps yet this option ignored and
		 * 						a new step is created.
		 * 						
		 * @return Item added.
		 */
		public function addStep(item:IPlayable, addToLastStep:Boolean=false):  
IPlayable {
			return (super._addStep(item, addToLastStep, SequenceStepCA));
		}
		
		/**
		 * Adds a single SequenceStepCA or IPlayable instance (LinearGo,  
PlayableGroup,
		 * etc.) at a specific index in the steps array. Calling this method  
stops any
		 * sequence play currently in progress.
		 *
		 * @param item		The playable item to splice into the sequence.
		 *
		 * @param index		Position in the array starting at 0, or a negative
		 * 					index like Array.splice.
		 * 					
		 * @return 			Item added.
		 */
		public function addStepAt(item:IPlayable, index:int): IPlayable {
			return (super._addStepAt(index, item, SequenceStepCA));
		}

		/**
		 * Removes and returns the SequenceStepCA at a specific index from the  
steps
		 * array. Calling this method stops any sequence play currently in  
progress.
		 *
		 * @param index		Position in the array starting at 0, or a negative
		 * 					index like Array.splice.
		 * 					
		 * @return 			The SequenceStepCA instance removed from the steps array.
		 */
		public function removeStepAt(index:int): SequenceStepCA {
			return (super._removeStepAt(index) as SequenceStepCA);
		}
				
		// -== IPlayable implementation ==-

		/**
		 * Begins a sequence.
		 *
		 * <p>If the group is active when this method is called, a  
<code>stop</code> call
		 * is automated which will result in a GoEvent.STOP event being  
dispatched.</p>
		 *
		 * @return Returns true unless there are no steps in the sequence.
		 */
		override public function start() : Boolean {
			return super.start();
		}
		
		/**
		 * Stops all activity and dispatches a GoEvent.STOP event.
		 *
		 * @return Returns true unless sequence was already stopped.
		 */
		override public function stop() : Boolean {
			if (super.stop()==false)
				return false;
			initTrailingSteps(false);
			return true;
		}
		
		/**
		 * Pauses sequence play.
		 *
		 * @return  Returns true unless sequence was unable to pause any children.
		 */
		override public function pause() : Boolean {
			var success:Boolean = super.pause();
			if (_trailingSteps!=null) {
				_trailingSteps.pause();
				if (_trailingSteps.state==PlayStates.PAUSED) {
					_state = PlayStates.PAUSED;
					success = true;
				}
			}
			return success;
		}
		
		/**
		 * Resumes previously-paused sequence play.
		 *
		 * @return  Returns true unless sequence was unable to resume any  
children.
		 */
		override public function resume() : Boolean {
			var success:Boolean = super.resume();
			if (_trailingSteps!=null) {
				_trailingSteps.resume();
				if (_trailingSteps.state==PlayStates.PLAYING) {
					_state = PlayStates.PLAYING;
					success = true;
				}
			}
			return success;
		}
		
		/**
		 * Stops current activity and skips to another step by sequence index.
		 *
		 * @return Always returns true since the index is normalized to the  
sequence.
		 */
		override public function skipTo(index : Number) : Boolean {
			if (_steps.length==0)
				return false;
			initTrailingSteps(false);
			return super.skipTo(index);
		}
		
		// -== Protected Methods ==-
		
		/**
		 * @private
		 * Internal handler for item completion.
		 * @param event		SequenceEvent dispatched by child item.
		 */
		override protected function onStepEvent(event : Event) : void {
			// A stop() call to the sequence results in step dispatching STOP, which  
would recurse here.
			if (_state==PlayStates.STOPPED)
				return;
			// trailing item
			if (_trailingSteps!=null && event.target==_trailingSteps &&  
event.type==SequenceEvent.ADVANCE) {
				initTrailingSteps(false);
				if (_steps.length-_index==1) {
					if (lastStep.state==PlayStates.STOPPED) {
						// A completed sequence was waiting for trailing steps to finish.
						// Otherwise, trailing items have finished before sequence ended so  
no action should be taken.
						complete();
					}
					else {
						// Special case where advance already fired but trailing steps have  
all completed: use COMPLETE
						lastStep.addEventListener(GoEvent.COMPLETE, onStepEvent);
					}
				}
				return;
			}
			
			// Finishes special case in trailing item block. Also, returns out if  
we're waiting
			if (lastStep.hasEventListener(GoEvent.COMPLETE)) {
				if (event.type==GoEvent.COMPLETE) {
					initTrailingSteps(false); // _trailingSteps is null, this is to remove  
the COMPLETE listener.
					complete();
				}
				return;
			}
			
			super.onStepEvent(event);
		}
						
		/**
		 * @private
		 * Internal handler for step advance.
		 */
		override protected function advance() : void {
			if (currentStep.listenerCount > 0) {
				initTrailingSteps(true);
				_trailingSteps.addChildAndAdoptState(currentStep);
			}
			super.advance();
		}
		
		/**
		 * @private
		 * Internal handler for group completion.
		 */
		override protected function complete() : void {
			if (_trailingSteps==null) {
				super.complete();
			}
		}
		
		/**
		 * @private
		 * Internal setup for tracking items that are continuing to run after a  
custom advance.
		 * @param active	Whether to create or destroy the trailing-steps group.
		 */
		protected function initTrailingSteps(active:Boolean):void {
			if (_trailingSteps==null && active) {
				_trailingSteps = new SequenceStep();
				_trailingSteps.playableID += "(_trailingSteps for  
sequence:"+playableID+")";
				_trailingSteps.addEventListener(SequenceEvent.ADVANCE, onStepEvent);
			}
			else if (!active) {
				lastStep.removeEventListener(GoEvent.COMPLETE, onStepEvent); // Remove  
special case set in onStepEvent.
				if (_trailingSteps!=null) {
					_trailingSteps.removeEventListener(SequenceEvent.ADVANCE, onStepEvent);
					_trailingSteps.stop();
					_trailingSteps = null;
				}
			}
		}
	}
}
\ No newline at end of file
+/**
  * Copyright (c) 2007 Moses Gunesch
  *
  * Permission is hereby granted, free of charge, to any person obtaining a  
copy
  * of this software and associated documentation files (the "Software"), to  
deal
  * in the Software without restriction, including without limitation the  
rights
  * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  * copies of the Software, and to permit persons to whom the Software is
  * furnished to do so, subject to the following conditions:
  *
  * The above copyright notice and this permission notice shall be included  
in
  * all copies or substantial portions of the Software.
  *
  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS  
OR
  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL  
THE
  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING  
FROM,
  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
  * THE SOFTWARE.
  */
package org.goasap.utils {
	import org.goasap.PlayStates;
	import org.goasap.events.GoEvent;
	import org.goasap.events.SequenceEvent;
	import org.goasap.interfaces.IPlayable;
	
	import flash.events.Event;	

	/**
	 * Sequence with "Custom Advance" options, in which steps can specify when  
they should advance.
	 *
	 * <p>This class works like Sequence but uses the special class  
SequenceStepCA for its steps.
	 * SequenceStepCA has a property called <code>advance</code>. When steps  
advance before animation
	 * finishes, the trailing steps are tracked so that the SequenceCA doesn't  
dispatch its COMPLETE
	 * event until all activity has completed.</p>
	 *
	 * <p>Any step's advance property can be set to an instance of  
OnDurationComplete, OnPlayableComplete,
	 * OnEventComplete or OnConditionTrue. Each of those classes defines its  
own parameters and rules for
	 * when the advance occurs. For example, using OnPlayableComplete a  
sequence can advance after one
	 * particular item in the step finishes, without needing to wait for all  
the other ones in that group
	 * to complete.</p>
	 *
	 * <p>Additionally, you can create your own custom advance types by  
subclassing the SequenceAdvance
	 * base class.</p>
	 *
	 * @see SequenceStepCA
	 * @see org.goasap.utils.customadvance.OnConditionTrue OnConditionTrue
	 * @see org.goasap.utils.customadvance.OnDurationComplete  
OnDurationComplete
	 * @see org.goasap.utils.customadvance.OnEventComplete OnEventComplete
	 * @see org.goasap.utils.customadvance.OnPlayableComplete  
OnPlayableComplete
	 * @see org.goasap.utils.customadvance.SequenceAdvance SequenceAdvance
	 * @see Sequence
	 * @see SequenceBase
	 *
	 * @author Moses Gunesch
	 */
	public class SequenceCA extends SequenceBase {
		
		// -== Public Properties ==-
		
		// Also in super:
		// length : uint   [Read-only.]
		// playIndex : int [Read-only.]
		// steps : Array
		// start() : Boolean
		// stop() : Boolean
		// pause() : Boolean
		// resume() : Boolean
		// skipTo(index:Number) : Boolean
		
		/**
		 * Returns the currently-playing SequenceStepCA.
		 * @return The currently-playing SequenceStepCA.
		 * @see #getStepAt()
		 * @see #getStepByID()
		 * @see #steps
		 * @see #lastStep
		 */
		public function get currentStep() : SequenceStepCA {
			return (super._getCurrentStep() as SequenceStepCA);
		}
		
		/**
		 * Returns the final SequenceStepCA in the current sequence.
		 * @return The final SequenceStepCA in the current sequence.
		 * @see #getStepAt()
		 * @see #getStepByID()
		 * @see #steps
		 * @see #currentStep
		 */
		public function get lastStep() : SequenceStepCA {
			return (super._getLastStep() as SequenceStepCA);
		}
		
		// -== Protected Properties ==-
		
		/**
		 * @private
		 */
		protected var _trailingSteps : SequenceStep;
		
		// -== Public Methods ==-
		
		/**
		 * Constructor.
		 *
		 * @param items		Any number of IPlayable instances (e.g. LinearGo,  
PlayableGroup,
		 * 					SequenceStepCA) as separate arguments, or a single array of them.
		 */
		public function SequenceCA(...items) {
			super((items[ 0 ] is Array) ? items[ 0 ] : items);
		}
		
		/**
		 * Retrieves any SequenceStepCA from the steps array.
		 * @param index		An array index starting at 0.
		 * @return			The SequenceStepCA instance at this index.
		 * @see #getStepByID()
		 */
		public function getStepAt(index:int) : SequenceStepCA {
			return (super._getStepAt(index) as SequenceStepCA);
		}
		
		/**
		 * Locates a step with the specified playableID. To search within a step  
for a
		 * child by playableID, use the step instance's <code>getChildByID</code>  
method.
		 *
		 * @param playableID	The step instance's playableID to search for.
		 * @return				The SequenceStepCA with the matching playableID.
		 */
		public function getStepByID(playableID:*) : SequenceStepCA {
			return (super._getStepByID(playableID) as SequenceStepCA);
		}
		
		/**
		 * Adds a single SequenceStepCA or IPlayable instance (LinearGo,  
PlayableGroup,
		 * etc.) to the end of the steps array, or optionally adds the instance  
into the
		 * last SequenceStepCA instead of adding it as a new step. Calling this
		 * method stops any sequence play currently in progress.
		 *
		 * <p>To remove a step use the <code>removeStepAt</code> method.</p>
		 *
		 * @param item			The playable item to add to the sequence. Note
		 * 						that when new steps are added, any IPlayable
		 * 						instance of a type other than SequenceStepCA is
		 * 						automatically wrapped in a new SequenceStepCA.
		 * 						
		 * @param addToLastStep	If true is passed the item is added to the last
		 * 						existing SequenceStepCA in the steps array. This
		 * 						option should be used with individual items that
		 * 						you want added as children to the SequenceStepCA.
		 * 						If there are no steps yet this option ignored and
		 * 						a new step is created.
		 * 						
		 * @return Item added.
		 */
		public function addStep(item:IPlayable, addToLastStep:Boolean=false):  
IPlayable {
			return (super._addStep(item, addToLastStep, SequenceStepCA));
		}
		
		/**
		 * Adds a single SequenceStepCA or IPlayable instance (LinearGo,  
PlayableGroup,
		 * etc.) at a specific index in the steps array. Calling this method  
stops any
		 * sequence play currently in progress.
		 *
		 * @param item		The playable item to splice into the sequence.
		 *
		 * @param index		Position in the array starting at 0, or a negative
		 * 					index like Array.splice.
		 * 					
		 * @return 			Item added.
		 */
		public function addStepAt(item:IPlayable, index:int): IPlayable {
			return (super._addStepAt(index, item, SequenceStepCA));
		}

		/**
		 * Removes and returns the SequenceStepCA at a specific index from the  
steps
		 * array. Calling this method stops any sequence play currently in  
progress.
		 *
		 * @param index		Position in the array starting at 0, or a negative
		 * 					index like Array.splice.
		 * 					
		 * @return 			The SequenceStepCA instance removed from the steps array.
		 */
		public function removeStepAt(index:int): SequenceStepCA {
			return (super._removeStepAt(index) as SequenceStepCA);
		}
				
		// -== IPlayable implementation ==-

		/**
		 * Begins a sequence.
		 *
		 * <p>If the group is active when this method is called, a  
<code>stop</code> call
		 * is automated which will result in a GoEvent.STOP event being  
dispatched.</p>
		 *
		 * @return Returns true unless there are no steps in the sequence.
		 */
		override public function start() : Boolean {
			return super.start();
		}
		
		/**
		 * Stops all activity and dispatches a GoEvent.STOP event.
		 *
		 * @return Returns true unless sequence was already stopped.
		 */
		override public function stop() : Boolean {
			if (super.stop()==false)
				return false;
			initTrailingSteps(false);
			return true;
		}
		
		/**
		 * Pauses sequence play.
		 *
		 * @return  Returns true unless sequence was unable to pause any children.
		 */
		override public function pause() : Boolean {
			var success:Boolean = super.pause();
			if (_trailingSteps!=null) {
				_trailingSteps.pause();
				if (_trailingSteps.state==PlayStates.PAUSED) {
					_state = PlayStates.PAUSED;
					success = true;
				}
			}
			return success;
		}
		
		/**
		 * Resumes previously-paused sequence play.
		 *
		 * @return  Returns true unless sequence was unable to resume any  
children.
		 */
		override public function resume() : Boolean {
			var success:Boolean = super.resume();
			if (_trailingSteps!=null) {
				_trailingSteps.resume();
				if (_trailingSteps.state==PlayStates.PLAYING) {
					_state = PlayStates.PLAYING;
					success = true;
				}
			}
			return success;
		}
		
		/**
		 * Stops current activity and skips to another step by sequence index.
		 *
		 * @return Always returns true since the index is normalized to the  
sequence.
		 */
		override public function skipTo(index : Number) : Boolean {
			if (_steps.length==0)
				return false;
			initTrailingSteps(false);
			return super.skipTo(index);
		}
		
		// -== Protected Methods ==-
		
		/**
		 * @private
		 * Internal handler for item completion.
		 * @param event		SequenceEvent dispatched by child item.
		 */
		override protected function onStepEvent(event : Event) : void {
			// A stop() call to the sequence results in step dispatching STOP, which  
would recurse here.
			if (_state==PlayStates.STOPPED)
				return;
			// trailing item
			if (_trailingSteps!=null && event.target==_trailingSteps &&  
event.type==SequenceEvent.ADVANCE) {
				initTrailingSteps(false);
				if (_steps.length-_index==1) {
					if (lastStep.state==PlayStates.STOPPED) {
						// A completed sequence was waiting for trailing steps to finish.
						// Otherwise, trailing items have finished before sequence ended so  
no action should be taken.
						complete();
					}
					else {
						// Special case where advance already fired but trailing steps have  
all completed: use COMPLETE
						lastStep.addEventListener(GoEvent.COMPLETE, onStepEvent);
					}
				}
				return;
			}
			
			// Finishes special case in trailing item block. Also, returns out if  
we're waiting
			if (lastStep.hasEventListener(GoEvent.COMPLETE)) {
				if (event.type==GoEvent.COMPLETE) {
					initTrailingSteps(false); // _trailingSteps is null, this is to remove  
the COMPLETE listener.
					complete();
				}
				return;
			}
			
			super.onStepEvent(event);
		}
						
		/**
		 * @private
		 * Internal handler for step advance.
		 */
		override protected function advance() : void {
			if (currentStep.listenerCount > 0) {
				initTrailingSteps(true);
				_trailingSteps.addPlayingChildAndStart(currentStep);
			}
			super.advance();
		}
		
		/**
		 * @private
		 * Internal handler for group completion.
		 */
		override protected function complete() : void {
			if (_trailingSteps==null) {
				super.complete();
			}
		}
		
		/**
		 * @private
		 * Internal setup for tracking items that are continuing to run after a  
custom advance.
		 * @param active	Whether to create or destroy the trailing-steps group.
		 */
		protected function initTrailingSteps(active:Boolean):void {
			if (_trailingSteps==null && active) {
				_trailingSteps = new SequenceStep();
				_trailingSteps.playableID += "(_trailingSteps for  
sequence:"+playableID+")";
				_trailingSteps.addEventListener(SequenceEvent.ADVANCE, onStepEvent);
			}
			else if (!active) {
				lastStep.removeEventListener(GoEvent.COMPLETE, onStepEvent); // Remove  
special case set in onStepEvent.
				if (_trailingSteps!=null) {
					_trailingSteps.removeEventListener(SequenceEvent.ADVANCE, onStepEvent);
					_trailingSteps.stop();
					_trailingSteps = null;
				}
			}
		}
	}
}
\ No newline at end of file



More information about the GoList mailing list