as3 declaring a GLOBAL variable - in TIMELINE / outside of CLASS - actionscript-3

I am looking to declare a GLOBAL VAR in the main time line.
Then I need to access that GLOBAL VAR from another externally loaded SWF's.
QUESTIONS:
How do I create the global var in the main timeline?
How do I access that var in externally loaded swf files?

First, you shouldn't use any global/static state. In your situation this is even more true, because Singletons are a royal pain in the butt across different applicationDomains.
Instead, you should use something called Dependency Injection. Think of your little swfs as starving orphans. When they have loaded, they don't run up to your main swf and pick its pockets. Instead, the main swf magnanimously presses money into their little hands.
So, how do we make this happen? One way is that we could compile a reference to their Document class(es) into the main swf, and then we could set a variable that the Class exposes. However, this can get pretty heavy and isn't really necessary.
Instead, you can write something called an Interface, which defines the "idea" of an orphan.
It might look something like this:
public interface IOrphan {
function get alms():Number;
function set alms(value:Number):void;
}
Note that you have to use getters and setters with Interfaces, because you can't use them to define vanilla variables. However, that's going to work out great for our actual Orphan:
public class Oliver implements IOrphan {
private var _alms:Number;
private var _totalAlms:Number;
public var tf:TextField;//put this on stage and allow Flash to populate automatically
public function get alms():Number {
return _alms;
}
public function set alms (value:Number):void {
_alms = value;
_totalAlms += _alms;
updateAlmsMessage();
}
private function updateAlmsMessage():void {
tf.text = 'That was a donation of ' + _alms + '.\n'
'I now have ' _totalAlms + '.\n'
'Please, sir, can I have some more?';
}
}
Now, all you need to do is populate that variable on load. There are several ways you can do this, such as watching the stage for IOlivers to be loaded, or you could be more direct about it:
private function loadSwf(url:String):void {
var loader:Loader = new Loader();
loader.contentLoaderInfo.addEventListener(Event.COMPLETE, completeHandler);
var request:URLRequest = new URLRequest(url);
loader.load(request);
addChild(loader);
}
private function completeHandler(e:Event):void {
((e.target as LoaderInfo).content as IOrphan).alms = .25;
}

If these are variables that you only want to set once and will never change, you can just create a class that holds static constants.
package
{
public class Env
{
public static const WHATEVER:String = "Whatever!";
public function Env()
{}
}
}
Then you could access them later in your program like so:
trace(Env.WHATEVER);
However, if you want global variables that can change, I like to handle this by using a singleton class.
package
{
import flash.events.EventDispatcher;
public class Control extends EventDispatcher
{
//---------------------------------------
// PRIVATE & PROTECTED INSTANCE VARIABLES
//---------------------------------------
private static var _instance:Control;
//---------------------------------------
// PUBLIC VARIABLES
//---------------------------------------
public var whatever:String = "Whatever";
//---------------------------------------
// PUBLIC METHODS
//---------------------------------------
public static function get instance():Control
{
return initialize();
}
public static function initialize():Control
{
if (_instance == null)
{
_instance = new Control();
}
return _instance;
}
//---------------------------------------
// CONSTRUCTOR
//---------------------------------------
public function Control()
{
super();
if (_instance != null)
{
throw new Error("Error:Control already initialised.");
}
if (_instance == null)
{
_instance = this;
}
}
}
}
The difference here is that you need to grab the instance of your singleton before you can get to what's inside it. It'd look a little bit like this.
private var _control:Control = Control.instance;
// Reading a global variable
trace(_control.whatever);
// Change a global variable
_control.whatever = "Foobar!";
So whenever you change "whatever", that variable will change for all loaded SWFs. If you want to be really fancy about it, you could use getters/setters in your singleton rather than simple public variables.

Related

Get stage in ActionScript-3 without a DisplayObject?

How can I get a reference to my stage without having a Sprite/DisplayObject that is added to the stage already ?
More info: I have a static class that is a utility class and I want it to initialize in static class constructor but I also need the reference to the stage.
public class UtilClass
{
trace("init: " + stage);
}
First thing that is called in my AS-3 apps is the constructor of my main Sprite/DisplayObject and it has access to the stage. So the stage exists at that point. Then I call utility methods of my UtilClass. Now I want it to initialize by itself on the first use (when stage is already in existance).
I want to know if stage object can be accessed from anywhere without being initialized from outside of the utility class.
Edit:
public class SimpleSprite extends Sprite
{
public static var aaa:int = 12;
public static function test():void
{
trace("here I am");
}
trace(aaa, Capabilities.screenResolutionX+", "+Capabilities.screenResolutionY);
test();
}
The stage reference is available in your MainTimeline or Main instance, depending on platform. You can add code there to pass that reference to other classes should you need it. The class should have a method (static, in your case) that'll accept a Stage parameter and store it somewhere inside the class.
public class UtilClass {
private static var theStage:Stage=null;
public static function initialize(s:Stage):void {
if (theStage) return; // we're initialized already
theStage=s;
}
// once you call this, you can "trace(theStage)" and get correct output
// other methods can also rely on theStage now.
}
Then you call UtilClass.initialize(stage); and you're set.
You will need to initialise your UtilClass and pass the stage reference. I recommend you to have a Class only for 'manage' Stage reference.
You could try something like this (just a quick example):
public class StageReference
{
public static const STAGE_DEFAULT:String = 'stageDefault';
protected static var _stageMap:Dictionary;
public static function getStage(id:String = StageReference.STAGE_DEFAULT):Stage
{
if (!(id in StageReference._getMap()))
throw new Error('Cannot get Stage ("' + id + '") before it has been set.');
return StageReference._getMap()[id];
}
public static function setStage(stage:Stage, id:String = StageReference.STAGE_DEFAULT):void
{
StageReference._getMap()[id] = stage;
}
public static function removeStage(id:String = StageReference.STAGE_DEFAULT):Boolean
{
if (!(id in StageReference._getMap()))
return false;
StageReference.setStage(null, id);
return true;
}
protected static function _getMap():Dictionary
{
if (!StageReference._stageMap) StageReference._stageMap = new Dictionary();
return StageReference._stageMap;
}
}
When you start your application (Main Class or where you start to include your logic)
StageReference.setStage(stage);
And when you need to get the stage reference
trace('Checking the Stage: ', StageReference.getStage());

AS3 How to add a Class on the stage from a timer?

lots of help from you guys :). My next question is here :).
I have timer in class MyTimer.as and Thief1_mc.as movie clip.
How can I addChild(Thief1_mc) on the stage from MyTimer? Everything looks simple, the only problem is "stage" property. MyTimer class cannot send "stage" as an argument because is not on the stage itself. I tried adding MyTimer on the stage in Main class like addChild (MyTimer), the trace says MyTimer is on the stage but I still cannot pass the stage argument to the Thief1_mc. I need this argument to be sent because the class Thief1_mc has to add itself on the stage using the property "stage".
The code:
public class Thief1_mc extends MovieClip
{
//this variable type Stage will contain stage
private var stageHolder:Stage;
public function Thief1_mc()
{
//constructor
}
//function that creates this object with passed "stage" argument from the caller
public function createItself(st):void
{
//variable that contain the stage so I can use this argument anywhere in the class
stageHolder = st;
//i have to refer to the stage by passed "st" parameter to create this object
stageHolder.addChild(this);
//initial position
this.x = 380;
this.y = 230;
}
}
}
MyTimer class and "_thief1.createItself(stage)" caller with stage arument
public class MyTimer extends Sprite
{
private static var nCount:Number = 120;
private static var currentCount:Number;
private static var _timer:Timer = new Timer(1000,nCount);
private static var _timerDispather:Timer;
private static var _thief1:Thief1_mc = new Thief1_mc ;
public function MyTimer()
{
// constructor code
}
//another timer
private static function increaseInterval(interval:int):void
{
_timerDispather = new Timer(interval);
_timerDispather.addEventListener(TimerEvent.TIMER, onUpdateTimeAnotherTimer);
_timerDispather.start();
}
//another timer;
private static function onUpdateTimeAnotherTimer(e:Event):void
{
_thief1.createItself(stage);//the most important part
}
public static function activateTimer():void
{
currentCount = nCount;
_timer.addEventListener(TimerEvent.TIMER, onUpdateTime);
_timer.start();
}
public static function deactivateTimer():void
{
_timer.removeEventListener(TimerEvent.TIMER, onUpdateTime);
_timer.stop();
_timer.reset();
currentCount = nCount;
//another timer
_timerDispather.removeEventListener(TimerEvent.TIMER, onUpdateTimeAnotherTimer);
_timerDispather.stop();
_timerDispather.reset();
}
private static function onUpdateTime(e:Event):void
{
currentCount--;
if (currentCount == 0)
{
_timer.removeEventListener(TimerEvent.TIMER, onUpdateTime);
_timer.stop();
_timer.reset();
}
}
}
}
Your code is backwards in a few places. It does not flow very nicely, and the issues you having now are going to be tenfold at some stage in your project.
Firstly, your MyTimer class should not be extending Sprite. It does not get rendered and does not represent anything graphically.
Secondly, your timer class is taking on more than it should. I would revise it to manage your timers and timer events only. Create a list within your timer class that will contain some other elements which can have a method triggers to do other stuff, like creating and adding Thief1_mc.
A simplified version of this might look like:
public class Updater
{
private var _timer:Timer;
private var _toUpdate:Vector.<IUpdatable> = new Vector.<IUpdatable>();
public function Updater()
{
_timer = new Timer(60);
_timer.start();
_timer.addEventListener(TimerEvent.TIMER, _notifyUpdatables);
}
private function _notifyUpdatables(e:TimerEvent):void
{
for each(var i:IUpdatable in _toUpdate)
{
i.update(this);
}
}
public function addUpdatable(updatable:IUpdatable):void
{
_toUpdate.push(updatable);
}
public function removeUpdatable(updatable:IUpdatable):void
{
var index:int = _toUpdate.indexOf(updatable);
if(index >= 0) _toUpdate.splice(index, 1);
}
}
From here we need to create an interface which we will implement on classes that we want to be able to call update() on each time the Updater timer ticks:
public interface IUpdatable
{
function update(updater:Updater):void;
}
Now what I would do in your case is have a class that does extend Sprite and manages the graphics of the application / game. It will implement the IUpdatable interface like I have described and also could deal with adding your Thief1_mc:
public class View extends Sprite implements IUpdatable
{
public function update(updater:Updater):void
{
// Create a Thief.
var thief:Thief = new Thief();
updater.addUpdatable(thief);
addChild(thief);
}
}
Your Thief can take advantage of the IUpdatable interface we have and be added to the update queue when it is created, as I've done above. Just to have a complete example, here's the Thief class:
public class Thief extends Sprite implements IUpdatable
{
public function update(updater:Updater):void
{
// Make this Thief so some stuff.
//
}
}
And here's how you can tie it all together in your document class:
public class App extends Sprite
{
private var _updater:Updater;
private var _view:View;
public function App()
{
_updater = new Updater();
_view = new View();
_updater.addUpdatable(_view);
stage.addChild(_view);
}
}
This might be a bit overwhelming at first, and seem like a lot of work, but you now have a nice clean foundation to add more elements easily.
Rather than having your one class trying to manage timers and add Thieves like you had initially, we've separated the responsibilities and tightened up the flow a little. The Updater deals purely with storing IUpdatable instances and calling their update() method each time the Timer within it ticks. The View class manages the graphics and will also add a Thief each time it is updated via the Updater. The View was added to the stage initially, so all you need to do is add the thieves into itself to have them show up.
If you take this and restructure how the timers work within Updater, I think you'll be where you wanted but with a significantly better understanding and structure.

action script 3 - simplifying the code by implementing the OOP (probably abstract hack)

I apologize in advance if my question is not clear, because I don't know how to put this.
What I am trying to do is to reduce few lines of repeated code by implementing various OOP methods/concepts.
The problem
I have few set of of classes which has initialization process. So, I am implementing an init() method in all those classes. From the calling class (main), these objects will be instantiated and init() method of each object is called in the the order and call some other process after all of them are initialized.
Something like this
public function mainClass(){
_obj1 = new Class1();
_obj1.init();
_obj2 = new Class2();
_obj2.init();
_obj3 = new Class3();
_obj3.init();
doSomething();
}
Well, its not a big deal, but some of the classes' init() methods are asynchronous and I need to add an event listener to get notified when they have finished initialization.
I tried that by extending EventDispatcher for each of those classes and dispatch event and handle it. I even implemented a logic to handle multiple asynchhnous calls by maintaining a counter.
It will be a painful job for me whenever I need to add a new class. I thought I could untilize OOP and reduce and simplify the code.
So I came up with some thing like this, which is currently not possible (abstract class).
abstract class Initializable
{
private var _callBack:Function;
//implement initializaton process in this method
function init(callback:Function=null):void;
protected function get callback():Function{
return _callBack;
}
protected function set callback(func:Function):void{
_callBack = func;
}
protected function onComplete():void{
if (_callBack){
_callBack(this);
}
}
}
This is the main problem for me, as you know abstract class is not allowed in AS3, and the "this" refers to the Initializer class but not its subclass I guess.
This is what I am asking for your help (for the hack)
I need it very much to make my system design simple and flexible, because I can extend the solution to allow mass synchronous initialization which will allow to easily queue up all objects in the order and call init() one after the other in the order in which they are added.
The mass initializer which takes care of handling the asynchronous job
public class MassInitializer
{
private var _objList:Array; //holds objects
private var _callBacks:Array;
private var _onComplete:Function;
public function MassInitializer()
{
_objList = new Array();
}
public function add(obj:Initializable,callback:Function=null):void{
_objList.push(obj);
_callBacks.push(callback);
}
public function init():void{
for (var i:int = 0;i < _objList.length;i++){
_objList.init(this);
}
}
private function onProgress(obj:Initializable):void{
//do updates here
for (var i:int;i<_objList.length;i++){
var obj:Initializable = _objList[i];
var fun:Function = _callBacks[i];
//update progress
if (fun){
fun(obj);
}
_callBacks.splice(i,1);
_objList.splice(i, 1);
}
if (_objList.length == 0){
onComplete();
}
}
private function onComplete():void{
_onComplete(this);
}
}
the main (manager/caller) class (ClassA, ClassB are subclasses of Initialzable class)
public class MainClass
{
private var _obj1:ClassA;
private var _obj2:ClassB;
public function MainClass()
{
_obj1 = new ClassA();
_obj2 = new ClassB();
}
public function init():void{
var initManager:MassInitializer = new MassInitializer();
initManager.add(obj1);
initManager.start();
}
}
probably I am trying to (or want to )implement an observer pattern, but I don't want to confuse you by saying it in advance. Oops I said it? please ignore.
You can emulate abstract classes in ActionScript by enforcing method overrides: Just throw an error if the "abstract" method is called. I like to also implement an interface, but that's not a must, of course:
public interface Initializable
{
function init (callback : Function = null) : void;
function get callback () : Function;
function set callback ( callback : Function ) : void;
}
public class AbstractInitializableImpl implements Initializable
{
private var _callBack:Function;
protected function init(callback:Function=null):void {
throw new Error ("You must implement the init() method!");
}
protected function get callback():Function {
return _callBack;
}
protected function set callback(func:Function):void {
_callBack = func;
}
protected function onComplete():void {
if (_callBack){
_callBack(this);
}
}
}
It's not a 'hack'. It's also very simple. Make each subclass implement an interface, instead of extend an abstract class.
Here is the adobe reference on AS3 interfaces.

Cleaning ArrayCollections

Initially I had ten arraycollections declared in a flex module, which I thought, were causing a memory leak. So I separated them in a single class, which I would clean using the "destroy" method I created inside it. Would this work?
I hate the question title excuse me. But I wouldn't write it like "Seducing the garbage collector"
[Bindable]
public class Cfd
{
private static var instance:Cfd = new Cfd();
private var _cfds:ArrayCollection = new ArrayCollection();
// Constructor
public function Cfd(){
if (instance) { throw new Error('Cannot create a new instance. Must use Cfd.getInstance().') }
}
public static function getInstance():Cfd{
return instance;
}
public function get cfds():ArrayCollection{
return _cfds;
}
public function set cfds(value:ArrayCollection):void{
_cfds = value;
}
public function destroy():void{
if(_cfds != null){
_cfds.removeAll();
}
}
}
Whenever you use Singletons like this, you pretty much guarantee a memory leak, because you're probably listening to the ArrayCollection (and maybe items within it) from all over the place. When you explicitly provide a reference to an object through a getter/setter pair, you can add the listener in the setter and removie it when the value is reset.
Check out http://www.developria.com/2010/08/rethinking-addeventlistener-an.html for more on what's happening.
For more on why you should avoid Singletons http://misko.hevery.com/2008/08/17/singletons-are-pathological-liars
Just null the object should do it unless you have listeners attached to it.
cfds = null;
I have never seen [Bindable] used on the class itself, so not sure what you are trying to do there.
package{
public final class Cfd{
private static var instance;
private var _cfds:ArrayCollection = new ArrayCollection();
public function Cfd(singletonEnforcer:MySingletonEnforcer){
if (instance) { throw new Error('Cannot create a new instance. Must use Cfd.getInstance().') }
}
public static function getInstance():Cfd{
if (instance == null)
instance = new MySingleton(new MySingletonEnforcer());
return instance;
}
// don't see a real need for setter/getters here
public static function get cfds():ArrayCollection{
return _cfds;
}
public static function set cfds(value:ArrayCollection):void{
_cfds = value;
}
// I wouldn't even use a destroy on this class since it is a singleton.
// just set the data to null Cfd.cfds = null
public static function destroy():void{
_cfds = null
}
}
}
//this is in Cfd.as but is outside the package block
class MySingletonEnforcer {}

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);
}