Remove a function from the stage - actionscript-3

I have a big issue with my code
I have a function called "delayCallFuntions":
function delayCallFuntions(delay: int, func: Function) {
var timer: Timer = new Timer(delay, 1);
timer.addEventListener(TimerEvent.TIMER, func);
timer.start();
}
And I used this function like below to make connection between 2 point in my screen:
delayCallFuntions(1, function (e: Event) {timer011(wireColor);});
And function "timer011" is making the connections:
function timer011(firstColor: int): void {
wireColor = firstColor;
//GRID is a class
//Path A to B
var PathAB: Grid;
PathAB = new Grid(4, 5, 20, 17, canvas, wireColor);
this.addChild(PathAB);
}
My problem is:
I have several of these functions like "timer012", "timer013", ... that they need to execute one after another.
When I go out of this scene and come back again, these is still some of these functions are working while I need them to start from the beginning and go one by one.
for example: when i come back, "timer011" is starting while "timer016" is also completing at the same time.
hope someone can help me as this problem made me frustrated.

Currently you are creating a whole new timer everytime you add a function. That timer will stay in memory because of the event listener, and since it's encapsulated in the function, you have no easy way to reference it again to stop them.
What would be a better approach, is to create just one timer globally referenced so you can stop it if needed.
Here is a way you could accomplish this:
//create an array that will hold all the functions you are planning on calling
var delayedFuncs:Array = [];
//this var will be used to store the current function that will be called next
var currentFuncObj:Object = null; //set it to null so it clears the value when you return to this frame
//create a single, global timer reference for everything
//don't initialize it here though
//if you revisit this frame, you don't want to create a whole new timer, but keep using the previous one
var funcTimer:Timer;
//if the timer has already been created (you've been to this frame before), stop it
if (funcTimer) {
funcTimer.stop();
}else {
//if you haven't been to this frame before, create the timer and add the listener
funcTimer = new Timer(1,1);
funcTimer.addEventListener(TimerEvent.TIMER, nextFunc, false, 0, true);
}
//this function adds items to your queue. I've added the ability to also include parameters
function delayCallFunctions(delay:int, func:Function, ... funcParams):void {
//add an object to the array that stores the function, delay, and any parameters to pass to that function
delayedFuncs.push({delay: delay, func: func, params: funcParams});
//if the timer hasn't started running yet, start it since we've added something
if(!funcTimer.running) nextFunc();
}
//this function runs when the timer completes
function nextFunc(e:Event = null):void {
//if we have an existing function to call, call it
if (currentFuncObj){
//invoke the function with the parameters
currentFuncObj.func.apply(null, currentFuncObj.params);
}
//if there are still items in the array, grab the next one
if(delayedFuncs.length > 0){
//array.shift grabs the first element in the array and removes it from the array
currentFuncObj = delayedFuncs.shift();
//reset the timer
funcTimer.reset();
//set the appropriate delay
funcTimer.delay = currentFuncObj.delay;
//start the timer again
funcTimer.start();
}
}
So now, you'd use by doing:
delayCallFunctions(3000, trace, "hello", "world", "I'll be traced 3 seconds from now");
delayCallFunctions(2000, trace, "I'll be called 2 seconds after the last one");
Or, with your specific code:
delayCallFuntions(1000, timer011, wireColor);
Now at any time (say you hit a button to go to change scenes), you can just stop the global timer.
funcTimer.stop();

Related

Resetting timer in AS3

I'm frustrated. I have a button that when it is clicked on, three timers start. It is no problem the first time the button is clicked on because it runs through the three things it is suppose to do. The problem happens when I click on another button to move away from that area of the stage and then click back on the same button again. If I click back too quickly and hadn't finished letting the timer run out, then I see the tail-end of my sequence... it is not resetting.
You'd think this would be easy... that I should be able to just put in a timer.reset() but it doesn't appear to be working. I can't put the reset call into the code because it keeps saying that the timer I am referencing doesn't exist (TypeError: Error #1009: Cannot access a property or method of a null object reference.)
I tried different ways to make sure the timer was called before it tried to reset it but I keep getting the #1009 error and the various ways I have tried using the reset appear to be not working. I don't know both why timer.reset() won't work and how to get around this error. Thanks,
else if (
event.target.name == "num6a"
)
{
trace("num6a clicked on");
TweenLite.to(shape, 1, {x:-608.55, y:-423.05});
close2.visible = true;
close2.addEventListener(MouseEvent.CLICK, checkforclose);
shape.grey6.visible=true;
counterSix++;
if(counterSix > 1){
trace("inside counterSix");
myDelay_sec6a.reset()
myDelay_sec6b.reset()
myDelay_sec6c.reset()
//if(myDelay_sec6a != null){
//trace("myDelay_sec6a is not null!");
//myDelay_sec6a.stop();
//myDelay_sec6a.reset();
//}
}
//section6 timing
var myDelay_sec6a:Timer = new Timer(1000,1);
myDelay_sec6a.addEventListener(TimerEvent.TIMER, goTosec6a);
//myDelay_sec6a.reset();
myDelay_sec6a.start();
function goTosec6a(event:TimerEvent):void
{
TweenLite.to(shape.section6_a, 2, {alpha:1});
trace("seconds 1: cutting edge title appears");
}
var myDelay_sec6b:Timer = new Timer(3000,1);
myDelay_sec6b.addEventListener(TimerEvent.TIMER, goTosec6b);
//myDelay_sec6b.reset();
myDelay_sec6b.start();
function goTosec6b(event:TimerEvent):void
{
TweenLite.to(shape.section6_b, 2, {alpha:1});
TweenLite.to(shape.section6_c, 2, {alpha:1});
trace("seconds 3: first paragraph appears");
}
Taking a peak at your code the way it is currently setup you are going to have quite some trouble down the road making any adjustments to it. I'm not sure if you are doing this in the timeline or in a separate .as file, but you could have a fundamental scoping issue. That is your timer variables are being created in a function which means they are local to that function, so next time you click this button the variable myDelay_whatever is going to be null referenced ( which means running any of its methods is out of the question ). Either way if this is or is not the case, I rewrote some of this that makes it a bit more flexible.
Some of the things I changed include:
Moved your timer functions to remove local scoping ( currently they were setup inside a function perhaps? Either way it's better to have them separate if they are getting used a lot ).
Setup a way to track your timers and remove them when the function is called again.
Added evaluation of current timers that are running before you attempt to add a new one.
Also just as a side note, I haven't tested a single bit of this code but hopefully this puts you on the right track.
close2.visible = true;
close2.addEventListener(MouseEvent.CLICK, checkforclose);
shape.grey6.visible=true;
counterSix++;
if(counterSix > 1){
trace("inside counterSix");
var i:int;
for ( ; i < _timerList.length; i++ ) {
//reset all the timers
_timerList[i].timer.reset();
}
//clear the list
_timerList.length = 0;
}
addTimer( "timer_sec6a", 1000 );
addTimer( "timer_sec6b", 3000 );
//addTimer( "timer_sec6c", 1000 );
Then these are the functions I added:
private function goTosec6a(event:TimerEvent):void {
TweenLite.to(shape.section6_a, 2, {alpha:1});
trace("seconds 1: cutting edge title appears");
}
private function goTosec6b(event:TimerEvent):void {
TweenLite.to(shape.section6_b, 2, {alpha:1});
TweenLite.to(shape.section6_c, 2, {alpha:1});
trace("seconds 3: first paragraph appears");
}
private function goTosec6b(event:TimerEvent):void {
TweenLite.to(shape.section6_b, 2, {alpha:1});
TweenLite.to(shape.section6_c, 2, {alpha:1});
trace("seconds 3: first paragraph appears");
}
//create this somewhere to kepe track of your timers, dont forget to instantiate it _timerList = new Vector.<Object>();`
private var _timerList:Vector.<Object>;
//set the name parameter to timer_sec6a, timer_sec6b etc and it will setup the timer event function for you
private function addTimer( name:String, delay:Number, repeat:int=1 ):void {
//check to see if timer already exists before creating another
if ( !evalCurrentTimers( name ) ) {
//create new timer
var timer:Timer = new Timer( delay, repeat );
timer.addEventListener( TimerEvent.TIMER, this["goTo" + name.split("_")[1]] );
_timerList.push( {timer: timer, name: name} );
}
}
private function evalCurrentTimers( timerName:String ):Boolean {
var i:int;
//check to see that we don't already have a timer with the same name
for ( ; i < _timerList.length; i++ ) {
if ( _timerList[i].name == timerName ) {
return true;
}
}
return false;
}

How to get the width of a MovieClip for a different frame instantly?

Is there a way to get the width of a MovieClip (that does have a name) on a different frame? I have tried to using .width and .getBounds(null).width, however, both of them will give me only the width of the current frame. I have tried to do gotoAndStop(frameiwant), but the information doesn't seem to be correct until at least the next frame
I would like to get the width of the frame instantly so I don't have to wait until the next frame for the width.
The only way I could think of doing this was to have an initial phase in your project which will:
Run through all of the frames in your timeline. Create an object which will hold information about the children in that frame. It can be called Frame.
Iterate over all the children that are added to the stage in that frame and add a definition object that describes that child. The description can be as basic or vast as you need. We can call this class an ObjectDefintion.
The downside of this process is that you need to wait for the FRAME_CONSTRUCTED event like #Larusso pointed out in his answer. This means that the frame actually has to finish rendering before you are able to get information about its children, which of course means you have to go through and render every single frame in your timeline during this phase. All you can really do to mitigate this problem is set the frameRate to something high and then set it back when you're done assessing all the frames.
I have set this up and it works well - I'll paste each class and try explain what they do.
So for your document class (or whichever MovieClip holds the frames you want to look at), I have this:
public class Main extends MovieClip
{
private var _userFrameRate:int;
private var _frames:Vector.<Frame> = new <Frame>[];
public function Main()
{
_userFrameRate = stage.frameRate;
stage.frameRate = 120;
addEventListener(Event.FRAME_CONSTRUCTED, _assess);
}
public function getFrame(index:int):Frame
{
return _frames[index - 1];
}
private function _assess(e:Event):void
{
var frame:Frame = new Frame(this);
_frames.push(frame);
if(currentFrame === totalFrames)
{
removeEventListener(Event.FRAME_CONSTRUCTED, _assess);
gotoAndStop(1);
stage.frameRate = _userFrameRate;
ready();
}
else play();
}
public function ready():void
{
// Start here.
// There is a MovieClip on frame 10 with the instance name 'test'.
// We can get the width of it like this.
trace( getFrame(10).define("test").property("width") );
}
}
This basically initializes the phase in which we will run over each frame in the MovieClip and assess its children. The ready() method is used as the entry point for your code post-assessment.
Next we have the Frame class, which serves to hold information about children related to a frame:
public class Frame
{
private var _main:Main;
private var _content:Object = {};
public function Frame(main:Main)
{
_main = main;
update();
}
public function update():void
{
_content = {};
for(var i:int = 0; i < _main.numChildren; i++)
{
var target:DisplayObject = _main.getChildAt(i);
// This will be explained below.
var definition:ObjectDefinition = new ObjectDefinition(target, "x", "y", "width", "height");
_content[target.name] = definition;
}
}
public function define(name:String):ObjectDefinition
{
return _content[name];
}
}
It's pretty straightforward - you give it a reference to Main so that it can check children that are existent within it each frame.
The ObjectDefinition class is also pretty straightforward, acting purely as a repository for data that you want to keep track of on each child of the frame:
public class ObjectDefinition
{
private var _definition:Object = {};
public function ObjectDefinition(target:DisplayObject, ...properties)
{
for each(var i:String in properties)
{
_definition[i] = target[i];
}
}
public function property(property:String):*
{
return _definition[property];
}
}
You'll notice that the constructor accepts the target DisplayObject that will be defined, as well as any amount of properties you want to keep track of as strings (see above within Frame for implementation).
Once complete, you can chain the methods Main.getFrame(), Frame.define() and ObjectDefinition.property() to get properties of children that will exist throughout the timeline. For example, if you have a MovieClip with the instance name square on frame 15 and you want to get its width and height, you can do this within .ready() like so:
var square:ObjectDefinition = getFrame(15).define("square");
trace(square.property("width"), square.property("height"));
Of course this process is not ideal - but unfortunately it is the only way I can see that what you want to achieve is possible.
You have to listen to a specific event before you can ask for the information.
clip.addEventListener(Event.FRAME_CONSTRUCTED, frameReadyHandler);
clip.gotoAndStop(frame);
function frameReadyHandler(event:Event):void
{
clip.removeEventListener(Event.FRAME_CONSTRUCTED, frameReadyHandler);
var width = clip.width;
}
The Frame constructed event is the first of several events that gets dispatched. It gets dispatches right before the frame script gets executed. You could also wait for the on enter frame event.
You could add an event listener for 1 millisecond and test if the previousWidth you had stored is different. If it is, there you go. If not, its probably listening to the same frame.
A 1 millisecond timer is not such a big deal, stop it if you don't need it, resume it if you do, else, keep it running constantly. When it changes, dispatch an event or whatever needs to happen.
If you know the maximum size of the MovieClip, you may try this:
// Create movie clip
var movie :MovieClip = new MovieClipWith3Frames();
// Move to second frame
movie.gotoAndStop(2);
// Create bitmap witch magenta background
var bd :BitmapData = new BitmapData(200, 200, false, 0xFF00FF);
// Draw second frame
bd.draw(movie);
// Found the bounds of shape
var movieBounds:Rectangle = bd.getColorBoundsRect(0xFFFFFF, 0xFF00FF, false);
trace(movieBounds); // (x=42, y=15, w=32, h=33)

How to stop on Enter Frame firing multiple times?

I'm trying to do a simple url requestion on enter frame, but it seems to be executing multiple times?
I tried removing the event handler on handleLoadSuccessful, that seems to stop it firing infinitely, but its still executing about 10 times before it stops.
How do I make it only fire once?
addEventListener(Event.ENTER_FRAME, onLoadHandler);
function onLoadHandler(event:Event) {
var scriptRequest:URLRequest = new URLRequest("http://ad.doubleclick.net/clk;254580535;19110122;t?http://www.gamespot.com.au/Ads/gswide/hp/1x1.gif");
var scriptLoader:URLLoader = new URLLoader();
var scriptVars:URLVariables = new URLVariables();
scriptLoader.addEventListener(Event.COMPLETE, handleLoadSuccessful);
scriptLoader.addEventListener(IOErrorEvent.IO_ERROR, handleLoadError);
scriptRequest.method = URLRequestMethod.POST;
scriptRequest.data = scriptVars;
scriptLoader.load(scriptRequest);
function handleLoadSuccessful($evt:Event):void {
trace("Message2 sent.");
removeEventListener(Event.ENTER_FRAME, onLoadHandler);
}
function handleLoadError($evt:IOErrorEvent):void {
trace("Message1 failed.");
}
}
ENTER_FRAME is fired x times per second, where x is the document frame rate.
The code within your listening function leads me to thinking that you don't actually need the event listener or the listening function at all - if you just want what's within that function called a single time, move everything within it outside on its own.
You could also move it all into an init() function and then call that once.
Also, your functions handleLoadSuccessful() and handleLoadError() should not be defined within another function (in your case onLoadHandler()).

One after an other

I'm having trouble making something happen over and over without a for loop. Take a look at this:
package {
import flash.display.Sprite;
import flash.events.Event;
public class Main extends Sprite {
public function Main() {
addEventListener("done", caller);
caller();
}
public function caller(e:Event = null):void {
trace("hello!");
dispatchEvent(new Event("done"));
}
}
}
sing this will get you an "Error #2094: Event dispatch recursion overflow." really fast. It will show that the event dispatcher and caller() are getting called inside of them selves, nesting until the error happens.
What I want to do is this:
"When caller() is done, call it again"
not:
"call caller() before it finishes"
Now, before people start suggesting using a timer to guess how long it will take or use ENTER_FRAME, This caller() will not have any graphic data and won't be connected to a Sprite and the time it takes to finish may vary greatly from call to call. I'm really looking for a way to run it only after it has completely finished.
Thanks for your help.
Thank you for your responses. I used Timer and still could overflow with too many calls and too short a timer interval. So I simplified and tried to just make an Event based for loop class (A class that operates like a for loop, but with events to avoid gobbling up all the resources) The solution was to call the function, on it's completion call the timer; on the timer's completion call the function again and bounce them off of each other. Basically:
call function
wait
call function
wait etc.
Even if the timer is set to 0 and it freezes the swf until the all the functions are called, the function will complete before running again.
try it out:
package {
import flash.display.Sprite;
public class Efl extends Sprite { // the main class
public function Efl() {
// make four functions...
function init (o:Object):void { // akin to the first part of the for loop
o.value = 0;
}
function condition(o:Object):Boolean { // like the condition portion of the for loop
if (o.value <= 100) {
return (true);
} else {
return (false);
}
}
function next(o:Object):void { // the increment part of a for loop
o.value++;
}
function statements(o:Object):void { // the body of the for loop
trace(o.value);
}
// put the four functions in one new EventForLoop
var test1:EventForLoop = new EventForLoop(init, condition, next, statements, 1); // delay is 1 ms
test1.start(); // set it into motion
// do it again all in one line - not pretty but it works
var test2:EventForLoop = new EventForLoop(
function (o:Object):void { o.value = 0; },
function (o:Object):Boolean { if (o.value <= 50) return (true); else return (false); },
function (o:Object):void { o.value++ },
function (o:Object):void { trace("> " + o.value) },
20); // delay in 100ms
test2.start(); // start it up
// if you try this out, the two will run intertwined since the delays are different.
}
}
}
Here is the Class that runs the loop:
package {
import flash.events.EventDispatcher;
import flash.events.Event;
import flash.events.TimerEvent;
import flash.utils.Timer;
public class EventForLoop extends EventDispatcher {
// functions to call when simulating the for loop
private var initializer:Function; // is run once at the start of the loop
private var condition:Function; // returns boolean to tell the loop to continue or not
private var step:Function; // the function that runs after the loop is complete
private var statements:Function; // the actual body of the loop
private var timeout:Timer; // the timer to avaoid overflows
private var operator:Object = new Object(); // this is an object to hold and pass values across all the sub loop functions. it is the parameter passed to all four functions
// some event constants
static const NEXT:String = new String("EFLNext");
static const DONE:String = new String("EFLDone");
// constructor just loads vars and sets up timer
public function EventForLoop (init:Function, cond:Function, stepper:Function, stat:Function, delay:Number = 0) {
initializer = init;
condition = cond;
step = stepper;
statements = stat;
timeout = new Timer(delay, 1);
}
// the mail loop function...
private function next(e:Event = null):void {
// Try this and the lone afte the loop:
// trace ("start statements");
if (condition.call(null, operator)) { // if the condition is still true...
statements.call(null, operator); // do the body statements of the loop
step.call(null, operator); // increment
dispatchEvent(new Event(EventForLoop.NEXT)); // dispatch the event so that thw wait can start
} else { // condition returns false??
dispatchEvent(new Event(EventForLoop.DONE)); // tell the event dispatcher the loop is done
removeEventListener(EventForLoop.NEXT, wait); // stop event listeners
timeout.removeEventListener(TimerEvent.TIMER_COMPLETE, next);
}
// trace ("finish statements\n");
// this line and the one before the if() will show that the functcion ends before starting again, even if the Timer wait 0ms
}
// very simple function that waits and ten triggers the loop again
private function wait(e:Event):void {
timeout.reset();
timeout.start();
}
// metod used to set the loop running
public function start():void {
initializer.call(null, operator); // use the initioalizer to set the operator Object
addEventListener(EventForLoop.NEXT, wait); // when the loops done, wait
timeout.addEventListener(TimerEvent.TIMER_COMPLETE, next); // when done waiting, loop again
next(); //do the first loop
}
}
}
You might want to experiment with flash.utils.setTimeout(). Put it at the bottom of caller() and have it set a timeout for itself. If you give it a very small timeout interval, it will asynchronously recurse the next time Flash gets the chance.
Alternatively, an ENTER_FRAME event will do more or less the same thing (except at extremely high framerates). Flash will delay the rendering of the next frame until all the processing logic on one frame has finished. Furthermore, Flash is single-threaded, so you can be guaranteed that two copies of your function will never run simultaneously.
I've got questions similar to some of the other responders. How often do you want the call to happen? If what you want is for the call to immediately repeat as soon as it finishes, no other part of your program will ever get a chance to execute.
Is this for an assignment?
If you don't want for loops, how about a while loop?
Trying to use timers could work but it gets messy. If you absolutely must user a Timer then have some boolean flag set to true/false if your function is still running. The timer event would see if your function is finished, if so then call it again.
I would use enterFrame... Flash is frame based... when your process is finished, you check if you still have time for another call to the function, if not, just wait for the next frame to come...
addEventListener("enterFrame", loop);
function loop(e) {
var maxtime=1000/stage.frameRate;
var t1=getTimer();
while(getTimer()-t1 < maxtime) {
myProcess();
}
}
OK, I know you said
his caller() will not have any graphic data and won't be connected to a Sprite
And
I'm really looking for a way to run it only after it has completely finished.
So I'll address those and then tell you an enterframe is the best solution :)
You don't need a graphical representation, or access to the stage to use a enter frame event listener. You can simply do the following:
var s:Shape = new Shape();
s.addEventListener(Event.ENTER_FRAME, caller)
private function caller():void
{
//do stuff
}
Above we simple create a shape to listen for the enter frame events, and thats all we use it for.
As for the second part, when code is being interpreted at runtime and it comes to a function, caller in this case, it won't execute another function, or line of code outside that function, until it has finished it. So you know that it will never execute again until it has finished the previous call.
So an enterframe (or a timer) are your best / only solutions.
What you want to do is dispatch a new event when the Caller() finishes that then calls caller again.
But your need to have a max loop counter otherwise you will just get a stack overflow error.
Don't forget to use weak reference to your event listeners, as it will alway unused object to get garbage collected and help your app run smoother + faster.
package {
import flash.display.Sprite;
import flash.events.Event;
public class Main extends Sprite {
public function Main() {
addEventListener("Call_ME_AGAIN", callCaller, false, 0, true );
caller();
}
private var _counter:int = 0;
private const LOOP_TIMES:int = 100;
public function caller(e:Event = null):void {
trace("hello!");
if (counter != LOOP_TIMES)
{
dispatchEvent(new Event("Call_ME_AGAIN"));
counter++;
}
else if (counter == LOOP_TIMES)
{ //reset the counter so it can happen again when you want
counter = 0;
}
}
public function callCaller(e:Event = null):void {
e.stopImmediatePropagation();
caller(null);
}
}
}

Dynamically Handling Events with Function Expression

I have a class which exposes literally dozens of events(before you get of on a tangent about whether that's good/bad design, just know that I didn't make that class). The event object of each event(eventParam in the below code) always has a toDebugString function, that basically creates a string containing all of the event object's property values:
propertyName1: propertyValue1
propertyName2: propertyValue2
propertyName3: propertyValue3
It works so far as creating all of the panels, with the title of each panel being the name of the event. However, the big problem is that all of events end up in the TextArea of the last panel. So there is something I don't understand about the anonymous method. It's as if each iteration of the loop uses the same function, and on the last iteration of the loop it decides that the debugPanel that was just created will be the one that all instances of that function will reference. In other words, a new unique debugSubPanel and TextArea is created in each iteration of the loop, but there is only one debugResponseListener event handler shared by all iterations of the loop. So my question is, how can I dynamically create the event handler function dynamically so that it stays associated with the debugSubPanel that I want it to?
public function debugPanelCreated(event:FlexEvent)
{
//iterate through all of the events exposed by mClient.ResponsesDispatcher
//where key is the name of the event
for (var key:String in mClient.ResponsesDispatcher.respMap)
{
//for each event, create a panel containing a text box
var debugSubPanel:Panel = new Panel();
debugSubPanel.title = debugSubPanel.label = key;
var debugSubPanelTextArea:TextArea = new TextArea();
debugSubPanel.addChild(debugSubPanelTextArea);
var debugResponseListener:Function =
function (eventParam :Object) : void
{
//use debugString function to write the properties
//of eventParam to the text box
debugSubPanelTextArea.text = eventParam .toDebugString();
};
//listen to this event:
mClient.ResponsesDispatcher.addEventListener(key,debugResponseListener);
//add the panel for this event
debugPanel.addChild(debugSubPanel);
}
}
Actionscript includes a feature called closures, which means that when you create an inner function and call it, the variables of its parent function are still available. (This is how debugResponseListener = function() ... works at all.) The issue is that a closure is only created when that function is called, and it uses the variable values from their last setting.
You can get around this by making a function that returns the listener function you want.
function makePanelListener(debugSubPanelTextArea:TextArea) : Function
{
return function(eventParam :Object) : void {
//use debugString function to write the properties
//of eventParam to the text box
debugSubPanelTextArea.text = eventParam .toDebugString();
}
}
and in your original code:
var debugResponseListener:Function = makePanelListener(debugSubPanelTextArea);
(There's a little explanation of what's going on in Explaining JavaScript scope and closures, look for the section called "The Infamous Loop Problem". More on closures at jibbering.)
This is the hack I came up with. I really don't like it, but it'll work for now. Open to suggestions still.
public class ResponseDispatcherToDebugStringHelper
{
public var textArea:TextArea;
public function responseToDebugStringHandler(eventParam:Object) : void
{
//use debugString function to write the properties
//of eventParam to the text box
textArea.text = eventParam.toDebugString();
}
}
public function debugPanelCreated(event:FlexEvent)
{
//iterate through all of the events exposed by mClient.ResponsesDispatcher
//where key is the name of the event
for (var key:String in mClient.ResponsesDispatcher.respMap)
{
//for each event, create a panel containing a text box
var debugSubPanel:Panel = new Panel();
debugSubPanel.title = debugSubPanel.label = key;
var debugSubPanelTextArea:TextArea = new TextArea();
debugSubPanel.addChild(debugSubPanelTextArea);
var helper:ResponseDispatcherToDebugStringHelper =
new ResponseDispatcherToDebugStringHelper();
helper.textArea = debugSubPanelTextArea;
//listen to this event:
mClient.ResponsesDispatcher.addEventListener(key,helper.responseToDebugStringHandler);
//add the panel for this event
debugPanel.addChild(debugSubPanel);
}
}