Home Contact

[July 28, 2005]

From C++ to ActionScript, Chapter 1: Enumerated Types, Generated

Filed under: ActionScript — @ 8:49 pm

Note: This post has been superseded by Doh! Enumerated Types Simplified.

Perhaps I'm being foolish, but as part of a "research" project, I'm attempting to translate a class library from C++ to AS2. So I came across an enum, and I was wondering what to do with it.

A bit of googling revealed that last year Darron Schall presented an implementation of enums in ActionScript: Enumerated Types in ActionScript. It's a great thing to have in AS (and may be provided natively by Macromedia in the future, as the keyword enum is reserved), but I wasn't quite satisfied with it because each enum is not a distinct type, but just an instance of Darron's EnumeratedType class that you have to create explicitly in code that's going to use it, like so:

days = new EnumeratedType("Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday");

I’ve built on Darron's class to create something a little more Type-y.

First of all, I wanted each enum to be a distinct type:

class DaysOfWeek extends EnumeratedType

And I wanted each value to be accessed as a member of the type, as DaysOfWeek.Sunday, for example. So once again (as in my previous post) I'm thinking Singleton:

private static var _instance:DaysOfWeek = undefined;

private function DaysOfWeek()
{
	super("Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday");
}

and access to each value is provided like so:

public static function get Sunday ():Number

{
	if (_instance == undefined)
	{
		_instance = new DaysOfWeek();
	}
	return _instance["Sunday"];
}

Now it would be tedious to write out all seven accessors. So I've written an enum generator in Perl, and here it is. (You'll want to replace the first line with the path to Perl in your environment.)

#!/c/Perl/bin/perl

# genEnum.pl
# This script generates an ActionScript class representing an enumerated type.
# Alan Shaw, 2005 http://nodename.com/blog/2005/07/28/jenny-noom/
# Based on (and requires) the EnumeratedType class by Darron Schall http://www.darronschall.com/weblog/archives/000097.cfm

sub usage {
	print STDERR "Usage: $0 className value1 value2 ...\n";
	exit;
}

usage() if (scalar @ARGV < 2);

$className = @ARGV[0];
shift;
@values = @ARGV;

$fileName = "$className.as";

if (-e $fileName) {
	print STDERR "File $fileName exists; overwrite? (type Y for yes):\n";
LINE:	while (<STDIN>) {
		last LINE if (/^Y$/);
		exit;
	}
}

print STDERR "Creating $fileName\n";

open(ASFILE, ">", "$fileName") or die "Can’t create $fileName: $!";

print ASFILE "import com.darronschall.weblog.EnumeratedType;\n\n";
print ASFILE "class $className extends EnumeratedType\n";
print ASFILE "{\n\n\n";
foreach $value (@values) {
	print ASFILE "public static function get $value ():Number\n";
	print ASFILE "{\n";
	print ASFILE "\tif (_instance == undefined)\n";
	print ASFILE "\t{\n";
	print ASFILE "\t\t_instance = new $className();\n";
	print ASFILE "\t}\n";
	print ASFILE "\treturn _instance[\"$value\"];\n";
	print ASFILE "}\n\n\n";
}
print ASFILE "private static var _instance:$className = undefined;\n\n\n";
print ASFILE "private function $className()\n";
print ASFILE "{\n";
print ASFILE "\tsuper(";
$i = 0;
while (@values[$i + 1]) {
	print ASFILE "\"@values[$i]\", ";
	$i++;
};
print ASFILE "\"@values[$i]\");\n";
print ASFILE "}\n";
print ASFILE "\n\n}\n";

close ASFILE;

[July 20, 2005]

Decoupling Again: Event Mugger, and getInstance() Considered Ugly

Filed under: ActionScript — @ 1:49 am

Having read several times that I ought to be using EventDispatcher, I decided to study it up and get with the program. The advantage, as I understood it, was that objects (or "guys" as they are technically known) in a running program can broadcast messages (or event notifications), and other guys can listen for those events and do whatever they need to do when the event occurs, and the broadcasters and listeners don’t have to have their hooks into each other. So I was rather surprised to find that in order for a guy to receive event notifications he has to sign up for them with the broadcaster, thus requiring that he have a reference to the broadcaster. That’s not what I’d call decoupling.

An obvious solution is to route all the events through a single guy who will broadcast them. Everyone knows where to find him, and no one needs to know who initiated an event just to be able to receive it. (Of course the initiator can identify himself in the data sent as part of the event so a listener can find him once he’s received it.) This calls for the Singleton pattern.

Now in C++ a constructor returns the new instance of the class, so you can implement Singleton by always returning the same guy from the constructor. Users of the class call new Thing; and no one is the wiser. But in ActionScript a constructor doesn’t have a return value, so you don’t have the chance to monkey with it that way. So the Singleton pattern is ordinarily implemented in ActionScript by a class that keeps its constructor private and provides a getInstance() method so others can get hold of the single instance of the class. Well, this works just fine, but it’s half in and half out of the closet. You’re not hiding your singular nature from anybody, Thing; you’re making them keep saying Thing.getInstance().doThis(); and Thing.getInstance().doThat(); So don’t be surprised if they start asking Thing.getInstance().howComeYoureStillSingle(). We can simplify the interface a bit by giving the class some static methods that have the same names as the instance methods and that each invoke the corresponding method on the single instance.

So here’s my EventMgr class:

import mx.events.EventDispatcher;

class EventMgr
{
static var version:String = "$Id: EventMgr.as,v 1.4 2005/07/16 01:11:46 ashaw Exp $";

/*
 * Function:	dispatchEvent
 * @param	eventObject - any object; must have a String property whose name is "type"
 *			and whose value is the name of the event;
 *			may have other properties holding data about the event, such as
 *			the event's originator
 */
public static function dispatchEvent(eventObject:Object)
{ EventMgr.getInstance().dispatchEvent.apply(EventMgr.getInstance(), arguments); }

/*
 * Function:	addEventListener
 * @param	event - name of the event
 * @param	listener - object or function
 */
public static function addEventListener(event:String, listener)
{ EventMgr.getInstance().addEventListener.apply(EventMgr.getInstance(), arguments); }

/*
 * Function:	renmoveEventListener
 * @param	event - name of the event
 * @param	listener - object or function
 */
public static function removeEventListener(event:String, listener)
{ EventMgr.getInstance().removeEventListener.apply(EventMgr.getInstance(), arguments); }

/****************************
 * End of public interface! *
 ***************************/

private function EventMgr()
{
	EventDispatcher.initialize(this);
}

private function dispatchEvent() {};
private function addEventListener() {};
private function removeEventListener() {};

private static var _instance:EventMgr = null;

private static function getInstance():EventMgr
{
	if (EventMgr._instance == null)
	{
		EventMgr._instance = new EventMgr();
	}
	return EventMgr._instance;
}

}

Having written the above, I found that iteration::two reached a virtually identical solution in the Flex context, documented at Separating ActionScript from MXML: Approach 6. They didn’t go the extra step of hiding getInstance(), and they modified the API a bit with a broadcastEvent() method that explicitly requires an eventName:String argument.

iteration::two’s EventBroadcaster implementation is just one part of the Cairngorm architecture for Flash and Flex RIA development: Architecting Flash Applications with Cairngorm. I suspect that as I progress with Flash I may arrive at more solutions that converge with those of other OO developers who’ve gone before. I’ve already seen this happen recently at FlashBelt in Minneapolis (really good conference by the way), where I recognized my own approach to using interfaces and composition in Joey Lott’s talk on design patterns in Flash. So I may as well speed things up by digging into Cairngorm. I’m not developing RIAs (yet!) but I’m still coming up against some of the same architectural issues, even in a photo gallery website. Yes, I’ve heard about not over-architecting things, and if I had to get this out in a week I’d just make it work without making my code quite so general — after all, I did my first Flash project (earlier this year: Sandi Fellman Photography) in AS1, where, having read Branden Hall and Samuel Wan’s OOP with ActionScript, and knowing I’d be switching to AS2 soon, I never attempted doing proper inheritance with all that __proto__ stuff. I’m not even sure who’s doing what in that application, what with all the global functions and all kinds of methods tacked onto MovieClip, but it works very well, and it’s even some kind of debuggable. I just don’t want to develop that way as a rule.

And it is my feeling that proper OO discipline benefits everyone — all the "guys" in the application, as well as myself as programmer, debugger, and customizer, and the client and putative users.

A great introduction to EventDispatcher usage and implementation: Phil Chung’s blog.

An interesting discussion of concerns about memory leaks when using Delegates, especially in the context of a singleton class like EventMgr: Delegates, be careful….

[July 19, 2005]

Chain of Functions

Filed under: ActionScript — @ 11:37 am

Today I found myself needing to trigger two functions in a button’s onRelease event handler. I wanted to keep the ability to install a handler function dynamically, just like you’d do when saying mc.onRelease = doSomething;, but I wanted to add the new function without displacing any previously installed handler. Also I hoped to keep the API simple. See what you think.

In my button’s constructor, I have some code that looks like this:

this.onRelease = FunctionChain.create();
this.onRelease.add(Proxy.create(this, this.broadcastSelectionEvent));

and later, in another part of the forest:

theButton.onRelease.add(buttonReleaseFunc);

And that’s it! When the button is clicked, both functions are triggered.

The FunctionChain class that makes this work is pretty simple, too:

import ascb.util.Proxy;

class com.nodename.utils.FunctionChain
{
static var version:String = "$Id: FunctionChain.as,v 1.1.1.1 2005/07/18 00:36:48 ashaw Exp $";

static function create():Function
{
	return (new FunctionChain())._execute;
}

/**************************
 * End of public interface!
 *************************/

private var _functions:Array;
private var _execute:Function;

private function FunctionChain()
{
	this._functions = [];
	this._execute = Proxy.create(this, this.executeChain);
	this._execute.add = Proxy.create(this, this.add);
}

private function executeChain():Void
{
	var numFuncs:Number = this._functions.length;
	for (var i:Number = 0; i < numFuncs; i++)
	{
		this._functions[i]();
	}
}

// func had better be created by Proxy or Delegate!
private function add(func:Function):Void
{
	this._functions.push(func);
}

}

Simple, yes, but the details may not be clear to everybody, so let’s take a look at the code. (If it’s all clear to you, just skip to the usage notes at the end.)

The heart of the class is just an Array of functions; the add(func) method just adds another function to the Array, and the executeChain() method executes each of the functions in turn. Nothing special there. It’s the stuff in the constructor and in the static create() method that lets us hide things away and simplify the API.

First the Array of functions is initialized to an empty Array. No problem. Then we set _execute, a Function variable, to refer to the executeChain method, but not directly. We use a Proxy, which carries a reference to the guy who’s going to execute the method as well as a reference to the method itself. This is important (stop me if you’ve heard this) because when the function finally gets called, we’re going to need to find that guy, and probably no one around is going to be pointing to him. And since it’s he himself who’ll be executing his own method, the method doesn’t have to be public.

The Proxy class is part of Joey Lott’s ascb library. Its usage is just like that of mx.utils.Delegate, except that Proxy also allows you to specify arguments that will be passed to the function when it runs. So I’ve gotten into the habit of using Proxy rather than Delegate.

OK, back to _execute. This, as I said, refers to the executeChain() method, which is just what we want our onRelease method in the button to do. We’ll be returning _execute to the button so he can set his onRelease handler to it. But there’s another piece we need to take care of, and that’s providing a way to add functions to the chain. So we create another Proxy, referring to the FunctionChain’s add() method, and we tack it right onto _execute. Remember in ActionScript a Function is an Object, so like any other Object it can take on properties — either data members or other Functions. (And since Function is a dynamic class, it can take on properties that were not declared in the class definition.) I believe it was pretty common to add properties to Function objects in AS1, but this is the first time I’ve found an excuse to do it in AS2.

So we’re going to return _execute to the button. We could provide an accessor method in FunctionChain, but we note that there’s really no need for the button to have a handle on the FunctionChain object at all, so we can just make the constructor private and wrap the whole interface up in one static function, create().

A couple of notes about usage:

First, it’s important that the functions you add to the FunctionChain also be created by Proxy or Delegate.

Second, you can use FunctionChain to build any function; there’s nothing about it that restricts it to Button or MovieClip event handlers.

[July 18, 2005]

TRACE (not trace) and the Debug Panel

Filed under: ActionScript — @ 6:17 pm

Back in May, Jesse Warden published a class that uses the TRACE function provided by the MTASC compiler to make nicely formatted output for Flashout: FAME Chronicles #2: TRACE (not trace) in MTASC.

I greeted MTASC and swfmill with open arms and cast flowers at their feet for liberating me from the Flash IDE, and I’m now a happy resident of the No-FLA Zone. I’m not a fan of IDEs in general, and I’m comfortable with my old-fashioned tools make, vi, and cvs. I especially do not want colored keywords or code completion in an editor. So I’m not feeling like jumping into FAMES just yet (or FLAMES either!).

Anyway, I’ve just gotten around to configuring AdminTool for my environment (thanks Chris and John for the good work making it compatible with MTASC!), and I expect to be using it regularly, but for simple trace output I’ve been quite happy with Keith Peters’ Flash Debug Panel. So I’ve modified Jesse’s class a bit so I can use it with the Debug Panel, thus simplifying the Debug Panel’s API as well as getting the extra debugging info that TRACE provides.

Here’s Keith’s documentation for the Debug Panel API:

Debug.trace("hello world"); // any type
Debug.trace(value1, "hello", _root); // any types separated by commas
Debug.traceObject(myComponent, n); // n is how many levels deep the trace will iterate
Debug.clear(); // clears the panel

Using my Tracer class, I’m able to say

TRACE("hello world"); // any type
TRACE(value1, "hello", _root); // any types separated by commas
TRACE(myComponent, n); // n is how many levels deep the trace will iterate

and my Tracer.coolTrace() method (kept Jesse’s name for it) figures out whether to call Debug.trace() or Debug.traceObject().

Here’s my Tracer:

class com.nodename.utils.Tracer
{
static var version:String = "$Id: Tracer.as,v 1.1.1.1 2005/07/18 00:36:48 ashaw Exp $";

static public function coolTrace(str):Void
{
	if (str instanceof Object)
	{
		objectTrace.apply(Tracer, arguments);
	}
	else
	{
		stringTrace.apply(Tracer, arguments);
	}
}

static public function objectTrace(str:Object,
				 depth:Number,
                                 classNameAndMethod:String,
                                 fileName:String,
                                 lineNumber:Number):Void
{
        var theString:String = "*****\t" + classNameAndMethod;
        theString += "::" + lineNumber + "\t*****\n";

        Debug.trace(theString);
        Debug.traceObject(str, depth);
}

static public function stringTrace(str:String,
                                 classNameAndMethod:String,
                                 fileName:String,
                                 lineNumber:Number):Void
{
        var theString:String = "*****\t" + classNameAndMethod;
        theString += "::" + lineNumber + "\t*****";

        Debug.trace(theString);
        Debug.trace(str);
}

}

[July 9, 2005]

Tween and Tweener

Filed under: ActionScript — @ 12:31 am

Here’s a class I’ve written as a way to handle multiple simultaneous tweens, and to decouple the tweening API from direct manipulation of object properties. I’ve used it to tween colors by having it change the red, green, and blue components; simply tweening the numerical color value would pass through wildly different colors along the way. I’ve used it to smoothly grow a mask from revealing part of an image to revealing the entire image by tweening the x, y, width, and height of the mask. The possibilities are quite free because the client of the Tweener class is able to handle the tween callbacks in any way it chooses. For example, in my color tweening callback I take the opportunity to check the luminance of the background color as it changes, and flip text from black to white or vice versa for greatest contrast. In resizing a picture frame, as the width and height change I redraw it and reposition neighboring UI elements as well.

First let me show a demo of the color tweening. Each time you click on "Change Color" the background is tweened to a random color and the text becomes either black or white for greatest contrast with the new background.




Here’s the relevant code:

I start off the Tweener like this, initializing the bg color to black:

public function initTweener():Void
{
	setWhiteText();
	this._bkgdColorTweener = new Tweener(
		this,		// object to notify
		"colorFill",	// callback function when any variable has changed
		"colorFill",	// callback function when all variables have reached targets
		1.0,		// duration of tweening
		undefined,	// easing function: default = Regular.easeInOut
		undefined,	// useSeconds: default = true
		0, 0, 0);	// initial values for variables
	this._luminance = 0;
}

Then I change the color like this:

public function changeColor(theColor:Number):Void
{
	var red:Number = theColor >> 16;
	var green:Number = (theColor & 0x00ff00) >> 8;
	var blue:Number = theColor & 0x0000ff;

	this._bkgdColorTweener.stopAndContinueTo(red, green, blue);
}

and here’s the callback (only one because we don’t do anything special when the target color is reached):

public function colorFill(red:Number, green:Number, blue:Number):Void
{
	this.fillWithColor((red << 16) | (green << 8) | blue, Stage.width, Stage.height);

	var newLuminance:Number = luminance(red, green, blue);

	if ((newLuminance > 128) && (_luminance <= 128))
	{
		setBlackText();
	}
	else if ((newLuminance <= 128) && (_luminance > 128))
	{
		setWhiteText();
	}
	_luminance = newLuminance;
}

By the way, I’m using the NTSC luminance formula, which is 0.299 * red + 0.587 * green + 0.114 * blue.

Here’s the example using Tweener to draw a picture frame (just a rectangle using moveTo and lineTo) that grows or shrinks to reach the right size for the jpg that’s about to be loaded into it:

private function initTweener(w:Number, h:Number):Void
{
	// Initialize tweener with initial values for my width and height respectively:
	this.tweener = new Tweener(this, "onSizeChanged", "onSizeReached",
		undefined, undefined, undefined,
		Number(w) + PictureFrame.frameBorder, Number(h) + PictureFrame.frameBorder);
}

and the callback functions look like this:

private function onSizeChanged(theWidth:Number, theHeight:Number):Void
{
	redrawFrame(theWidth, theHeight);
	creator.onPictureFrameSizeChanged(this);
}

private function onSizeReached(theWidth:Number, theHeight:Number):Void
{
	onSizeChanged(theWidth, theHeight);

	this.adjustArrowVisibilities();
	showDisplayer();
}

I’m not showing all the code these functions invoke but I think the idea is clear.

Here’s the class:

import mx.transitions.Tween;
import mx.transitions.easing.*;
import ascb.util.Proxy;

/**
 * Tweener class
 *
 * This class executes tweens for one or more variables simultaneously.
 * The variables must be of type Number.
 * They do not need to directly represent properties of any object.
 *
 *
 * The public interface consists of four methods:
 *
 * the constructor, in which the caller specifies:
 * 	the client to notify,
 * 	the names of the methods the client will execute when
 * 	notified of events generated by the Tweener object, and
 * 	the initial values for the variables;
 *
 * stop(): stop all the tweens;
 *
 * stopAndContinueTo(), in which the caller specifies
 * 	the new value to move toward for each of the variables
 * 	(the caller must specify these values in the same
 * 	order as in the constructor!).
 *
 * rewind(): rewind all the tweens to their intial values;
 *
 * After either the constructor or stopAndContinueTo has been
 * called, the Tweener object begins generating two types of events:
 *
 * 	onPropChanged, when ANY variable's value has changed, and
 * 	onPropsReached, when ALL target values have been reached.
 *
 * The client must implement two functions to handle these events,
 * taking the values of the variables as arguments.
 * (The events are generated by the constructor even though the
 * values are not actually changing.  This allows the client to
 * initialize itself in the same way it modifies itself later on --
 * by responding to the events.  If such initialization is not needed,
 * you can call stop() immediately after the constructor.)
 *
 * This class was motivated by the need to dynamically resize
 * an on-screen object which updates its appearance using the
 * drawing API.  Allowing a Tween to directly modify the object's
 * _height or _width interacts poorly with the simultaneous use of the
 * drawing API.
 */

class com.nodename.utils.Tweener extends Object
{
static var version:String = "$Id: Tweener.as,v 1.11 2005/07/10 05:57:03 ashaw Exp $";

private var client:Object;
private var tweened:Object;
private var propReached:Array;
private var tweens:Array;
private var changedCallback:Function;
private var reachedCallback:Function;
private var easingFunc:Function;
private var duration:Number;
private var useSeconds:Boolean;

/**
 * Tweener constructor
 *
 * @param:	client		the Object that will be notified of changes to the variables' values
 * @param:	changedFunc	the name of the method that the Object will execute
 *				when any variable's value has changed
 * @param:	reachedFunc	the name of the method that the Object will execute
 *				when all the variables' values have reached their targets
 * @param:	duration	how long the tween takes to run
 * @param:	easingFunc	easing method
 * @param:	useSeconds	true: duration is number of seconds; false: duration is number of frames
 * @param:	prop1Init	the initial value for the first variable
 * @param:	prop2Init	the initial value for the second variable
 * @param:	etc, for the desired number of variables
 */
function Tweener(client:Object, changedFunc:String, reachedFunc:String,
		duration:Number, easingFunc:Function, useSeconds:Boolean,
		prop1Init:Number, prop2Init:Number)
{
	this.client = client;
	this.changedCallback = client[changedFunc];
	this.reachedCallback = client[reachedFunc];

	if (duration == undefined)
	{
		this.duration = 0.5;
	}
	else
	{
		this.duration = duration;
	}

	if (easingFunc == undefined)
	{
		this.easingFunc = Regular.easeInOut;
	}
	else
	{
		this.easingFunc = easingFunc;
	}

	if (useSeconds == undefined)
	{
		this.useSeconds = true;
	}
	else
	{
		this.useSeconds = useSeconds;
	}

	arguments.splice(0, 6);
	init.apply(this, arguments);
}

/**
 * init
 *
 * @param:	prop1Init	the initial value for the first variable
 * @param:	prop2Init	the initial value for the second variable
 * @param:	etc, for the desired number of variables
 *
 * The client’s changedFunc and reachedFunc methods will be invoked
 * as notifications while this method is running.
 */
private function init(prop1Init:Number, prop2Init:Number):Void
{
	this.tweened = new Object(); // a private object whose props the tweens will act on
	this.tweens = [];
	this.propReached = [];
	var arglen:Number = arguments.length;
	for (var i:Number = 0; i < arglen; i++)
	{
		propReached[i] = false;

		// Create the tween and let it run with a very short duration:
		// (setting duration to zero would make it run indefinitely!)
		tweens[i] = new Tween(tweened, "_prop" + i, this.easingFunc,
					arguments[i], arguments[i], 0.01, this.useSeconds);

		// Allowing the newly-created tweens to play now without calling stop()
		// permits the client to initialize its own state with the initial values.

		tweens[i].onMotionChanged = Proxy.create(this, onPropChanged, tweens[i]);
		tweens[i].onMotionFinished = Proxy.create(this, onPropReached, tweens[i]);
	}
}

public function destroy():Void
{
	var numTweens:Number = tweens.length;
	for (var i:Number = 0; i < numTweens; i++)
	{
		delete tweens[i];
	}
	delete tweens;
	delete tweened;
	delete propReached;
}

/**
 * stopAndContinueTo
 *
 * @param:	prop1Target	target value for the first variable
 * @param:	prop2Target	target value for the second variable
 * @param:	etc, for the desired number of variables
 *
 * The client's changedFunc and reachedFunc methods will be invoked
 * as notifications while this method is running.
 */
public function stopAndContinueTo(prop1Target:Number, prop2Target:Number):Void
{
	var numReached:Number = propReached.length;
	var numTweens:Number = tweens.length;
	if (arguments.length != numTweens)
	{
		TRACE("Error: Tweener:stopAndContinueTo(): wrong number of arguments");
		return;
	}

	stop();
	for (var i:Number = 0; i < numReached; i++)
	{
		propReached[i] = false;
	}
	for (var i:Number = 0; i < numTweens; i++)
	{

		if (tweens[i] == null)
		{
			TRACE("Error: Tweener has not been initialized");
			return;
		}
		tweens[i].continueTo(arguments[i], this.duration);
	}
}

public function stop():Void
{
	var numTweens:Number = tweens.length;
	for (var i:Number = 0; i < numTweens; i++)
	{
		tweens[i].stop();
	}
}

public function rewind():Void
{
	var numTweens:Number = tweens.length;
	for (var i:Number = 0; i < numTweens; i++)
	{
		tweens[i].rewind();
	}
}

private function onPropReached(theTween:Object):Void
{
	var tweenIndex:Number = indexOf(theTween);
	if (tweenIndex == null)
	{
		TRACE("Error: Tweener:onPropReached(): tween not found");
		return;
	}

	propReached[tweenIndex] = true;
	if (allPropsReached() == true)
	{
		onPropsReached();
	}
}

private function indexOf(theTween:Object):Number
{
	var tweenIndex:Number = null;
	var numTweens:Number = tweens.length;
	for (var i:Number = 0; i < numTweens; i++)
	{
		if (tweens[i] == theTween)
		{
			tweenIndex = i;
			break;
		}
	}
	return tweenIndex;
}

private function allPropsReached():Boolean
{
	var numReached:Number = propReached.length;
	for (var i:Number = 0; i < numReached; i++)
	{
		if (propReached[i] == false)
		{
			return false;
			break;
		}
	}
	return true;
}

private function onPropChanged(theTween:Object):Void
{
	var argArray = [];
	var numTweens:Number = tweens.length;
	for (var i:Number = 0; i < numTweens; i++)
	{
		argArray[i] = tweened["_prop" + i];
	}

	changedCallback.apply(client, argArray);
}

private function onPropsReached():Void
{
	var argArray = [];
	var numTweens:Number = tweens.length;
	for (var i:Number = 0; i < numTweens; i++)
	{
		argArray[i] = tweened["_prop" + i];
	}

	reachedCallback.apply(client, argArray);
}

}

Contents copyright © Alan Shaw 2005-2008

25 queries. 0.337 seconds. Powered by WordPress version 2.5.1