Accessing Function on MovieClip from Document Class - actionscript-3

[NOTE: Right off the bat, I already know the popular theory about "no code on MovieClips," so please don't reply with that (or
downvote because of it). Every project is different. I just need the answer.]
I have a project in Adobe Flash CS5.5, Adobe AIR 3, and ActionScript 3.
I need to call a function on the main timeline of the project that the document class is linked to from inside the document class. For the sake of example, let's call this function "Ring()".
How do I call this function, "Ring()" from inside my document class?

Put the function you want to call in your document class, and dispatch a custom event (or any event, if the code is readable) from the timeline of the object and listen for that event on your document class.
So the code breakdown would look like this:
On some frame of the timeline in your document (should work on any object):
var customEvent:Event = new Event(Event.COMPLETE);
this.dispatchEvent(customEvent);
In your document class:
public function DocumentClass()
{
// get the reference to the object
lolcats.objectThatDispatchesEvent.addEventListener(Event.COMPLETE, _customHandler);
}
protected function _customHandler(event:Event):void
{
// FUNCTION NAMES SHOULD START WITH LOWERCASE! ^_^
Ring();
}
http://www.adobe.com/devnet/actionscript/articles/event_handling_as3.html
Basically you register any string that defines your event, Event.COMPLETE evaluates to "complete", you can just register whatever you want, like:
var custEvent = new Event("anyCustomString");
this.dispatchEvent(custEvent);
// catch it with
addEventListener("anyCustomString", _handler);

Well, since you seem to be using some oldskool ninja techniques, I would suggest that you should keep it simple and straightforward.
Say you have some functions on the main timeline:
function Ring1():String
{
return "Ring1() called!";
}
var Ring2:Function = function () : String
{
return "Ring2() called!";
};
The scenario for a document class of the said timeline would be like this:
package {
import flash.display.MovieClip;
import flash.events.MouseEvent;
import flash.utils.getQualifiedClassName;
import flash.utils.describeType;
public class Test extends MovieClip
{
public function Test()
{
stage.addEventListener(MouseEvent.MOUSE_DOWN, onMouseDown);
}
private function onMouseDown(event:MouseEvent):void
{
trace(getQualifiedClassName(this)+".onMouseDown()");
try {
var ring1:Function = this["Ring1"] as Function;
var ring2:Function = this["Ring2"] as Function;
} catch (error:Error) {
// ignore
}
if (ring1 != null) {
trace("\t", "Ring1", "=", ring1);
trace("\t", ring1());
} else {
trace("\t", "Ring1() function not found in "+this+"!");
}
if (ring2 != null) {
trace("\t", "Ring2", "=", ring2);
trace("\t", ring2());
} else {
trace("\t", "Ring2() function not found in "+this+"!");
}
// for your interest:
var doc:XML = describeType(this);
var ring1Node:XML = doc.descendants("method").(#name == "Ring1")[0];
var ring2Node:XML = doc.descendants("variable").(#name == "Ring2")[0];
trace("declaration of Ring1:", ring1Node.toXMLString());
trace("declaration of Ring2:", ring2Node.toXMLString());
// so, you may probably make use of reflection
// unless you need to reference dynamic members on the main timeline
}
}
}
See comments in the code above.

Related

Access of undefined property issues in AS3

I am having a bit of trouble with some AS3. First time using this language and have more experience with web development then OOP so am getting a bit confused.
I am trying to make it so that when someone clicks a 'powerbutton' which is a "movieclip" symbol within flash then another symbol should then become visible. This is all being done within the Kitchen class.
The code for the main class is which i got from a youtube tutorial video i followed;
package {
import flash.display.MovieClip;
import flash.events.MouseEvent;
import flash.geom.Point;
import flash.events.Event;
import Kitchen
public class DragFood extends MovieClip
{
protected var originalPosition:Point;
var myKitchen:Kitchen
public function DragFood() {
myKitchen = new Kitchen;
originalPosition = new Point (x, y);
buttonMode = true;
addEventListener (MouseEvent.MOUSE_DOWN, down);
}
protected function down (event:MouseEvent):void
{
parent.addChild(this);
startDrag();
stage.addEventListener (MouseEvent.MOUSE_UP, stageUp);
}
protected function stageUp (event:MouseEvent):void
{
stage.removeEventListener (MouseEvent.MOUSE_UP, stageUp);
stopDrag();
if (dropTarget)
{
if(dropTarget.parent.name == "bowl")
{
trace("The " + this.name + " is in the bowl");
this.visible = false;
} else {
returnToOriginalPosition();
}
} else {
returnToOriginalPosition();
}
}
protected function returnToOriginalPosition():void
{
x = originalPosition.x;
y = originalPosition.y;
}
}
}
Within it i call the other class;
import Kitchen
public class DragFood extends MovieClip
{
protected var originalPosition:Point;
var myKitchen:Kitchen
The code for the kitchen class is;
package {
import flash.display.MovieClip;
import flash.events.MouseEvent;
import flash.events.Event;
public class Kitchen extends MovieClip
{
// This is a function. This particular function has the same name as our class and therefore will be executed first
public function Kitchen()
{
// This is a "call" to another function that is defined later in the program.
init();
trace("Hello world");
}
public function init():void
{
// If we want an object (on the screen or otherwise) to be notified about an event we must add a listener for that event to that object.
// We also need to specify what happens everytime the event we are listening for happens.
PowerButton.addEventListener(MouseEvent.CLICK, handleButtonClicks);
}
//This function is called when the oven on button recieves a click.
public function handleButtonClicks(event:MouseEvent):void
{
OvenOn.visible = true;
trace("the oven is being switched on");
}
}
}
The issue i keep getting is that OvenOn and PowerButton are giving me a undefined access issue and im not sure how to fix it. I have found posts on similar subjects like - Access of Undefined property? Actionscript 3
but im not quite sure how to apply it to my issue if anyone could offer any help that would be great.
When you're programming on the timeline, code is referencing the local namespace, and objects you make there (movieclips, textfields, etc.) are automatically instantiated in that namespace so that you can simply call OvenOn.visible = true. However, for each class, their local namespace is whatever is inside the class, so unless you actually created a property on your class called OvenOn, it will most definitely give you Access of Undefined Property errors.
Think of each class as its own island. For them to touch eachother, they need some sort of connection. That connection can be made once the parent instantiates the class in its own namespace. For example...
var foo:String = "Hello!";
var bar:MyClass = new MyClass();
// At this point, whatever code runs inside of MyClass has no concept of foo, or how to access it.
addChild(bar);
// Now that we've added it to the stage, the bar has some properties that have automatically been populated such as "root", "parent", or "stage".
foo.someProperty = "World";
// Since this namespace has a variable pointing to the instance, we can change properties on that class.
Now that we've instantiated MyClass on the stage, we can reference parent properties the class didn't know about. Mind you, this is not necessarily best practice.
package
public class MyClass extends MovieClip {
var someProperty:String = "cheese";
public function MyClass() {
trace(parent.foo) // this will fail
addEventListener(Event.ADDED_TO_STAGE, test);
}
public function test(e:Event):void {
trace(this["parent"].foo); // this will succeed
}
}
}
If you absolutely must change something that is not part of your Kitchen class, pass either the parent of OvenOn or that object specifically as a property of Kitchen. You could do this a couple ways.
with the Constructor...
var something:*;
public function MyClass(someObject:*) {
something = someObject;
}
public function test():void {
something.visible = false;
}
...or by Assigning the Property...
var bar:MyClass = new MyClass();
bar.something = OvenOn;
bar.test(); // will turn off the OvenOn now that 'something' is pointing to it.

How to improve this AS3 code structure to be more effective?

I have made an AS3 code to be a function. But I think my code is too lengthy. Could you help to improve it? Thank you!
I created test.fla first and added 5 grey block(external pictures from PSD) to stage. My function is to display different pictures when hovering mouse on corresponding grey block.
I converted those 5 grey blocks to Movie Clip and set instance name as sp1, sp2, sp3, sp4 and sp5. Then I created a document class, test.as and set 5 EventListener.
sp1.addEventListener(MouseEvent.MOUSE_OVER,clickmouse1);
sp2.addEventListener(MouseEvent.MOUSE_OVER,clickmouse2);
sp3.addEventListener(MouseEvent.MOUSE_OVER,clickmouse3);
sp4.addEventListener(MouseEvent.MOUSE_OVER,clickmouse4);
sp5.addEventListener(MouseEvent.MOUSE_OVER,clickmouse5);
So my first question is can I have any method to combine those 5 EventListener to be one? Because in my mind, so many EventListener will cost much more resource of PC.
My second question is I set 5 target pictures as 5 class.
In test.as I created code below:
public class EuroCup extends Sprite{
var arr:Array=new Array();
var Res1:Result609=new Result609();
var Res2:Result610=new Result610();
var Res3:Result611=new Result611();
var Res4:Result612=new Result612();
var Res5:Result613=new Result613();
var i:int=0;
public function EuroCup() {
arr[1]=Res1;
arr[2]=Res2;
arr[3]=Res3;
arr[4]=Res4;
arr[5]=Res5;
}
}
I think that is too lengthy. Is there any way to simplify it?
Here is the test.fla and test.as:Download
Whatever, thank u guys!
Restructuring:
public class EuroCup extends Sprite {
private var arr:Array;
public function EuroCup() {
arr = [ new Result609(), new Result610(),
new Result611(), new Result612(), new Result613()
];
}
}
Then use results as arr[0], arr[1] and so on. Also, if you have several sprites to listen clicks on, with similar listeners, you can connect all such sprites to single listeners and use event.target to distinguish them, where event is MouseEvent. Or place them into container and create one listener to that container - again, event.target will tell what sprite is clicked.
And yet two things - every time you see new Array(), replace it with [] - its faster and shorter. And place all code into constructor, not class body - it will be compiled to be executed faster.
You can/should use a Dictionary for associations between the grey rects and the images to display.
package {
public class EuroCup {
private var _children:Array, _current:Sprite, _map:Dictionary;
public function EuroCup() {
super();
initialize();
}
protected function initialize():void {
_children = [];
_map = new Dictonary();
// i don't know the image's symbol name.
// _map[_children[_children.length] = new Result609()] = new SYMBOL_NAME();
for each(var child:Sprite in _children) {
child.addEventListener(MouseEvent.CLICK, click_handler);
}
}
private function click_handler(event:MouseEvent):void
{
if (_current) {
_current.visible = false; // or use fading, etc
}
_current = _map[event.currentTarget] as Sprite;
if (_current) {
_current.visible = true; // or use fading, etc
}
}
}
}
One option for simplifying the code would be to associate the sp and Res instances with each other by identity, using a Dictionary. That allows you to avoid the work of tracking array indices, which is half of the reason you have separate event handler methods. Once the instances are associated by identity, then you can use the currentTarget property of a dispatched event to determine which element in the Dictionary you want to show on the stage.
package {
import flash.display.MovieClip;
import flash.display.Sprite;
import flash.events.MouseEvent;
import flash.events.Event;
import flash.utils.Dictionary;
public class test extends Sprite
{
var dict:Dictionary = new Dictionary();
var visibleResult:MovieClip;
public function test()
{
dict[sp1]=new Result609();
dict[sp2]=new Result610();
dict[sp3]=new Result611();
dict[sp4]=new Result612();
dict[sp5]=new Result613();
sp1.addEventListener(MouseEvent.MOUSE_OVER,clickmouse);
sp2.addEventListener(MouseEvent.MOUSE_OVER,clickmouse);
sp3.addEventListener(MouseEvent.MOUSE_OVER,clickmouse);
sp4.addEventListener(MouseEvent.MOUSE_OVER,clickmouse);
sp5.addEventListener(MouseEvent.MOUSE_OVER,clickmouse);
}
private function clickmouse(evt:MouseEvent):void
{
if(visibleResult)
{
removeChild(visibleResult);
}
var Res:MovieClip = dict[evt.currentTarget] as MovieClip;
addChild(Res);
Res.x=300;
Res.y=400;
visibleResult=Res;
}
}
}
If you expect to have more than 5 sp instances in the application, then you could use a loop to assign the event listeners. But for less than 10 instances, you probably don't gain much from a loop.
I would go for a more simple version; add only one event listener and use Event.target to determine on which item is clicked, using a switch-statement.
This is helpful if the buttons should do different things.
package
{
import flash.display.Sprite;
import flash.events.MouseEvent;
public class Test extends Sprite
{
public var sp1:Sprite;
public var sp2:Sprite;
public var sp3:Sprite;
public function Test()
{
this.addEventListener(MouseEvent.MOUSE_OVER, handleClick);
}
private function handleClick(event:MouseEvent):void
{
trace("Clicked on: " + event.target)
switch (event.target)
{
case this.sp1:
{
// do something here
break;
}
case this.sp2:
{
// do something here
break;
}
case this.sp3:
{
// do something here
break;
}
default
{
trace("No handler defined for: " + event.target)
}
}
}
}
}
However, you can also make smart use of it's type. Let's say all you buttons extend a custom class called CustomButton, and they all need to do the same (like call a function), but with a parameter based on it's id.
This is helpful if the buttons should basically do the same thing.
package
{
import flash.display.Sprite;
import flash.events.MouseEvent;
public class Test extends Sprite
{
public function Test()
{
this.addEventListener(MouseEvent.MOUSE_OVER, handleClick);
}
private function handleClick(event:MouseEvent):void
{
if (event.target is CustomButton)
{
var button:CustomButton = event.target as CustomButton; // you're now sure it's a CustomButton
this.showById(button.id); // let's say CustomButton has a public var 'id'
}
}
private function showById(id:int):void
{
// do something
}
}
}
Hope that helps.
Tip: Always start your class+filename with a capital. Variables start with capitals. This is very common in the actionscript world.

Re-defining named functions at runtime

What I am trying to do is kind of odd, but I am wondering if anyone can come up with a clever way to do what I want to do. Basically, I want to re-define a named function at runtime. I can do this with anonymous functions, but I can't figure out a way to do it for named functions. I want to do this so that I can implement a "spy" functionality on an object for a testing framework (a port of Jasmine to Flex).
Take, for instance, this class:
public class TestClass
{
public var anonymous:Function = function():void {
trace("original anonymous");
};
public function named():void {
trace("original named");
}
}
I can easily re-define the anonymous function because it is just a variable. Javascript uses this idiom a lot.
var testClass:TestClass = new TestClass();
testClass.anonymous = function():void { trace("overridden anonymous"); }
BUT, when I do the same thing for named functions, you get a compile-time error:
// Does not compile
testClass.named = function():void { trace("overridden named"); }
I tried to make it a bit more "squishy" but this leads to a runtime failure "Cannot assign to a method named on TestClass".
// Compiles with runtime failure
testClass["named"] = function():void { trace("overridden named"); }
Can anyone more clever than I come up with a way to hack this? Can the bytecode be hijacked? Something?
I want to modify an object, not a
class
But object doesn't contain functions, only non-static variables. I tried to use prototype property and replace method there, but original method still gets called instead of injected one.
About "hack" bytecode, do you mean "hack" already loaded SWF in runtime? I think it's not possible. I'm sure, though, you can parse SWF with something like as3swf, find method in bytecode, replace it and save result in new SWF.
I had an idea bout making a function "cache" . This might work with what you need.
Let's say you have a class "Car" with a method you need to redefine at runtime:
public class Car extends Sprite
{
private var functionCache:Function;
public function Car()
{
super();
}
public function flexibleFunction(functionBody:*=null):void{
if(functionBody is Function){
functionBody.call();
functionCache=functionBody;
} else {
functionCache(functionBody);
}
}
}
Usage:
public class Main extends Sprite
{
private var car:Car;
public function Main()
{
car = new Car();
car.flexibleFunction(function(){trace("redefine test #1")});
car.flexibleFunction();
car.flexibleFunction(function(doParametersWork:String="let's see"){trace("redefine test #2: " + doParametersWork);});
car.flexibleFunction("yes they do");
car.flexibleFunction();
}
}
an easy way to accomplish what you want is to simply pass a new function to the original function and execute it from there:
package
{
//Imports
import flash.display.Sprite;
//Class
public class RedefineFunction extends Sprite
{
//Constructor
public function RedefineFunction()
{
originalFunction();
originalFunction(redefinedFunction);
}
//Original Function
public function originalFunction(redefinition:Function = null):void
{
if (redefinition != null)
redefinition();
else
trace("Original Function Definition");
}
//Redefined Function
private function redefinedFunction():void
{
trace("Redefined Function Definition")
}
}
}
traces:
Original Function Definition
Redefined Function Definition

AS3 - Event listener that only fires once

I'm looking for a way to add an EventListener which will automatically removes itself after the first time it fires, but I can't figure a way of doing this the way I want to.
I found this function (here) :
public class EventUtil
{
public static function addOnceEventListener(dispatcher:IEventDispatcher,eventType:String,listener:Function):void
{
var f:Function = function(e:Event):void
{
dispatcher.removeEventListener(eventType,f);
listener(e);
}
dispatcher.addEventListener(eventType,f);
}
}
But instead of having to write :
EventUtil.addOnceEventListener( dispatcher, eventType, listener );
I would like to use it the usual way :
dispatcher.addOnceEventListener( eventType, listener );
Has anybody got an idea of how this could be done?
Any help would be greatly apprecitated.
(I know that Robert Penner's Signals can do this, but I can't use them since it would mean a lot of code rewriting that I can't afford for my current project)
I find the cleanest way without using statics or messing up your code with noise is to defining a global function (in a file called removeListenerWhenFired.as) like so:
package your.package
{
import flash.events.Event;
import flash.events.IEventDispatcher;
public function removeListenerWhenFired(callback:Function, useCapture:Boolean = false):Function
{
return function (event:Event):void
{
var eventDispatcher:IEventDispatcher = IEventDispatcher(event.target)
eventDispatcher.removeEventListener(event.type, arguments.callee, useCapture)
callback(event)
}
}
}
Then you can listen for events like so:
import your.package.removeListenerWhenFired
// ... class definition
sprite.addEventListener(MouseEvent.CLICKED,
removeListenerWhenFired(
function (event:MouseEvent):void {
... do something
}
)
)
I've not tried it, but you could just turn the EventUtil static method into a standard method and extend the class in your object.
public class OnceEventDispatcher
{
public function addOnceEventListener(eventType:String,listener:Function):void
{
var f:Function = function(e:Event):void
{
this.removeEventListener(eventType,f);
listener(e);
}
this.addEventListener(eventType,f);
}
}
public class Example extends OnceEventDispatcher
{
}
var ex:Example = new Example();
ex.addOnceEventListener(type, func);
functionadd.addEventListener(COMPLETE,functionremove);
functionremove()
{
runevent();
functionadd.removeEventListener(COMPLETE,functionremove);
}
function runevent()
{
trace('Hello');
}

Creating a custom trace() class in AS3

I got this idea of expanding my trace() messages.
Why
trace() is all over my code, I want to turn them on/off by a simple command and maybe add some sort of priority functionality to the trace(), i.e.
myTrace.TraceMsg("loosehere",debugme, 0);
myTrace.TraceMsg("winhere",debugme, 1);
And when I run, only the one with the higher priority, "1" in this case, shows.
There is a lot more functionality I would like to add as well, like logging messages to file and so on.
Problem
How do trace() work? -Is it possible to overload trace() somehow? -How would I implement the custom TraceMsg(what code here?) method?
Having some serious problems finding info on this subject on our favourite search engine, so any help would be appreciated.
I have come up with a rather efficient, yet tedious way of using my own trace() function in Flash only projects, but calling it simply with
trace("this", "that", "and that too");
I basically implement one trace() method in every class of my project, that calls a public function (so that i can call the real trace() function from there.
here is what I do : in every class I call this
include "trace_implementation.as";
in the .as file comes a simple method implementation (it could be a static method too).
public function trace(... arguments){
for(var i in arguments){
myTrace(arguments[i]);
}
}
and the myTrace function is defined in its own myTrace.as file
package pt.utils{
import flash.external.ExternalInterface
public function myTrace(_s:String):void{
trace(_s);// this will call the original flash trace() function
ExternalInterface.call("console.log", _s);// to get traces outside of flash IDE
/*implement what you want here*/
}
}
so now when I compile with "omit trace actions", my whole debugging is ignored as if I used trace() simply.
the really good part here is that you could implement custom actions depending on instructions you give in the trace, so :
trace(Debug.DEBUG_MESSAGE, "message to output in debug");
trace(Profile.START_PROFILING, this, 'name');
/*do heavy code*/
trace(Profile.STOP_PROFILING, this);
then dispatch it from myTrace, or a Tracer class or anything :)
Hope this helps future tracers.
trace() itself is a top-level function, not a class, so unfortunately we cannot extend it. That being said, we can utilize it in a simple class to do just what it does normally, only in this case the trace is based on conditions (i.e. Boolean - true|false, etc). First we create the Trace class, which we wouldn't instantiate ourselves because we are utilizing a Factory design pattern through the class below, Tracer. Tracer is built around the singleton design pattern, yet utilizes the Factory pattern to instantiate instances of Trace, when the trace method of Tracer is called.
//This class is handled by Tracer, which is right below it.
//You WILL NOT instantiate these, nor hold references.
package
{
public class Trace
{
private function _value:*;
private function _trace:Boolean;
public function Trace(pValue:*, pTrace:Boolean):void
{
_value = pValue;
_trace = pTrace;
}
public function get value():*
{
return _value;
}
public function get trace():Boolean
{
return _trace;
}
}
}
//This is the important class and the only one you will work with.
package
{
/**
*Utilizes Singleton and Factory design patterns.
*/
public class Tracer
{
private var _traceArray:Array;
private static var _instance:Tracer;
public function Tracer(pvt:PrivateClass = null):void
{
if(pvt == null)
{
throw(new Error("You cannot instantiate this class directly, please use the static getInstance method."));
}
_init();
}
public static function getInstance():Tracer
{
if(Tracer._instance == null)
{
Tracer._instance = new Tracer(new PrivateClass());
}
return Tracer._instance;
}
public function trace(pValue:*, pTrace:Boolean):void
{
var trace:Trace = new Trace(pValue, pTrace);
if(trace.pTrace)
{
trace(pValue);
}
}
//Since we have the option for individual traces to be disabled
//I provide this to get access to any and all later.
public function traceAll():void
{
traceStr:String = _traceArray.toString();
}
public function get traceables():Array
{
return _traceArray;
}
//Here we provide a method to trace all, even if set to false in their constructor.
private function _init():void
{
_traceArray = new Array();
}
}
}
//Here we create a class that is OUTSIDE of the package.
//It can only be accessed from within this class file. We use this
//to make sure this class isn't instantiated directly.
class PrivateClass
{
function PrivateClass():void
{
trace('can only be accessed from within this class file');
}
}
//Now for use in doc class
package
{
import flash.display.Sprite;
import flash.events.Event;
//No need to import Tracer and Trace, they are also in the
//unnamed package.
public class DocumentClass extends Sprite
{
private var _tracer:Tracer;
public function DocumentClass():void
{
if(stage) _init();
else addEventListener(Event.ADDED_TO_STAGE, _init);
}
private function _init(e:Event = null):void
{
_tracer = Tracer.getInstance();
_tracer.trace(10*20, false);
_tracer.trace(10*20, 0); //SAME AS ABOVE
_tracer.trace("I love AS3", true); //traces
_tracer.traceAll(); //Would trace: 200, 200, I love AS3
}
}
}
Keep in mind this is off the hip and very well could have a bug or two, but the idea is there; That is to say that this is not tested, it is merely to give you an idea of how you might implement this.
I hope this helps.
Look at the Flex logging API, particularly the section: Implementing a custom logger with the logging API.
Look up the TraceTarget class as well.
You can't override trace itself, but for ease of typing I like to create a global function called 'tr'. It's a little known fact that you can create global functions in AS3, but it's easy.
Create a file called tr.as inside you main source directory (not in a subdirectory or package), with the contents:
package {
public function tr(msg:String, ...):void {
// add custom trace logic here
trace("tr message: "+msg);
}
}
If you need to have a lot of logic or static storage variables etc, it might be better to make a separate static class, and have the global tr function call out to that, such as:
package {
import org.code.MyTracer;
public function tr(msg:String, ...):void {
MyTracer.tr(msg); // all the tracing logic is inside the MyTracer class
}
}
Here is a super simple custom trace function I use. debugFlag can be set to true/false
elsewhere in the package.
public static function myTrace(... vars) :void {
if (debugFlag) {
var output:Array = new Array;
for each (var arg in vars) {
output.push(arg);
}
trace(output);
}
}
In AS2, it was possible to override the global trace function by doing something like this (taken from memory, might be a bit wrong but the gist of it is there):
public static var realTrace:Function = _global["trace"];
// This is put in some init code somewhere
_global["trace"] = myTrace;
public static function myTrace(... args):void
{
// Do whatever you want with args here, build a nice formatted string or whatever
// before passing to realTrace. Using with MTASC one could add line numbers, class
// names and all sorts of nice meta data. Or just return should you want to turn
// tracing off.
realTrace.apply(args);
}
Unfortunately I haven't found a way to do the same in AS3. Yet.
Trace is a top-level function, so you can't override it, and as far as I know, it does not fire any events. Since it's a top-level function (not contained in any named package), you can use it without import statements.
Here is an example of a top-level "Tracer" class that you can use in place of trace without import statements.
Just call "Tracer.write" or "Tracer.writeError" for tracing Error objects.
"Tracer.write" accepts a variable number of arguments, just like the built-in trace function. "Tracer.writeError" is a helper method that allows you to easily trace Error objects.
Features:
Calls built-in trace.
Keeps a log of all your calls to Tracer.write as an array of strings.
The call log is accessible as a string through getText, which joins all elements in the array with a newline character and will optionally tack on line numbers!
Fires events when new lines are added to the log, so if you have some kind of display window for the log, the display window can listen for Tracer events to update the log display in real-time as the events occur. This is great for displaying trace events when running inside a web browser or stand-alone player.
-Tracer class definition
package
{
import flash.events.EventDispatcher;
public class Tracer extends EventDispatcher
{
private static var traced_text:Array = new Array( "--Start of Trace Log--" );
public static var enabled:Boolean = true;
private static var suspended:Boolean = false;
public static var instance:Tracer = new Tracer();
public static const newline:String = "\n"; //workaround for TextField.appendText bug.. use "\n" instead of "\r". See note and link to bug post in getText method
public function Tracer()
{
}
static public function write( ...args ):void
{
if (enabled && !suspended)
{
trace.apply( null, args );
var text:String = args.join( newline );
var next_index:int = traced_text.length;
traced_text.push( text );
suspended = true; //prevent recursive calls from TracerEvent handler
instance.dispatchEvent( new TracerEvent( text, next_index ) );
suspended = false;
}
}
static public function writeError( e:Error ):void
{
write( "errorID: " + e.errorID, "errorName: " + e.name, "errorMessage: " + e.message, "stackTrace: " + e.getStackTrace() );
}
static public function getText( include_line_numbers:Boolean ):String
{
var line_count:int = traced_text.length;
var lines:Array = traced_text; //store pointer to traced_text; pointer may be changed to reference an altered array that includes line numbers
if (include_line_numbers) //create temporary trace log copy with altered lines; allows quick call to join at end
{
var new_lines:Array = new Array();
for (var i:int = 0; i < line_count; i++)
new_lines.push( i.toString() + ": " + lines[i] );
lines = new_lines;
}
return lines.join( newline ); //do not include last newline character (workaround for bug in appendText method (https://bugs.adobe.com/jira/browse/FP-1982); I have to call appendText with newline character first, otherwise it has issues like not acknoledging the newline thats already there at the end).
}
static public function addEventListener(type:String, listener:Function, useCapture:Boolean = false, priority:int = 0, useWeakReference:Boolean = false):void
{
instance.addEventListener(type, listener, useCapture, priority, useWeakReference);
}
static public function removeEventListener(type:String, listener:Function, useCapture:Boolean = false):void
{
instance.removeEventListener(type, listener, useCapture);
}
static public function willTrigger(type:String):Boolean
{
return instance.willTrigger(type);
}
static public function hasEventListener(type:String):Boolean
{
return instance.hasEventListener(type);
}
}
}
-TracerEvent class definition
package
{
import flash.events.Event;
public class TracerEvent extends Event
{
public static const WRITE:String = "te_write";
public var text:String;
public var index:int; //index of newly traced text in the traced_text array (trace log)
public function TracerEvent( text:String, index:int )
{
super( WRITE, false, false );
this.text = text;
this.index = index;
}
override public function clone():Event
{
return new TracerEvent( text, index );
}
}
}
As mentioned below, there is no way to override trace (at least not if you want your traces to reach the output stream), but it's actually very easy to create your own universally accessable logging function. Plus, you can even define a universally accessable boolean to turn logging on or off:
log.as (note that the filename must reflect the name of the function)
package {
function log(... arguments):void {
trace("Custom logging FTW!");
if (logEnabled)
trace(arguments);
}
}
logEnabled.as (note that the filename must reflect the name of the variable)
package {
var logEnabled:Boolean = true;
}
Main.as
package {
import flash.display.MovieClip;
public class Main extends MovieClip {
public function Main() {
log("Testing");
logEnabled = false;
log("Testing2");
}
}
}
Response
Custom logging FTW!
Testing
Custom logging FTW!
you dont need to override it , just create a function in your project and call it trace then any trace call will point to this.trace ;)
function trace(... arguments){
yourfunction(arguments);
}