Posts tagged with “tips” and “source”

February 11, 10

Including imports

Do you remember the #include "filename.as" AS2 directive? It was often used to include predefined prototype functions in AS2. Since using prototypes become obsolete in AS3 (in favour of the inheritance) the include in often underused.

For example, Darron Shall suggests using include directive to mimic multiple inheritance. Recently, I had been given a glimpse of brain damage enlightenment and I started using it again in AS3 for including all project specific libs only. If you want to pass me the hammer now, please read on.

How?

Using wildcards (like import flash.display.*) is often considered a bad practice (or laziness) as we don't know what classes/packages are actually being imported. So we are left with typing all import directives for every class we write. Very commonly we use the same shared functionality (either a framework or built-in classes) in the SWF. The idea is that instead of having to import the same classes in every project class, we can have a list of them in a file, i.e.:

import flash.display.Bitmap;
import flash.display.MovieClip;
import flash.display.Sprite;
import flash.events.Event;
import flash.events.MouseEvent;
import flash.text.TextField;
import flash.text.TextFieldAutoSize;
import flash.utils.setTimeout;
import gs.TweenLite;

and then stop worrying anymore about what needs to be imported.

All you need to do is to place include "../../imports.as"; where your import directives would normally live. I've personally chosen to put it in the same location as the source directory.

package net.blog2t
{
    include "../../imports.as";

    public class Banner extends Sprite
    {
    }
}

Then you can do it for every project specific class you add.

If you're on OS X and using Textmate, you can use the shortcut – F5 key to sort lines and remove duplicated entries.

06:00 PM | | 2 Comments | Tags: , , ,
June 12, 09

The end of anonymous clicks (but don't be afraid)

Recently Google has enabled events tracking for all Analytics accounts – that opens a whole new world for the click hunters – just imagine how useful it might be for usability testing – you can find out if anyone is actually interacting with the new interface that took you 3 weeks to develop.
The official Google Analytics for Flash guide covers pretty much everything what you need to know to set it up for your SWFs and it's all easy peasy when you've got ga.js handy on your HTML page.

But what if you want to give your SWF away (by providing the embed code that links to your SWF) and still be able to track the referrer? You don't know who will be embedding your SWF thus you can't tell if the ga.js will be available.

Fortunately Google covers that matter as well – all you need to do is to import the whole AnalyticsTracker class (which is 100% compatible with the latest ga.js tracking code – at least that's what Google says) available at http://code.google.com/p/gaforflash, set the MODE to AS3 and stop worrying about any Analytics specific Javascript on the HTML page.

All pretty. The only thing is that there's no (direct) way of getting the referrer URL of the SWF that generated the event into your stats, so you won't know who had embedded your file. You could check loaderInfo.url but this will ONLY return the SWF's host location aka your site address and that's no use.

How to get the URL of a page that embeds my SWF then?
There's a simple trick – the page URL is stored in window.location.href and you can use ExternalInterface to get that out. It's actually pretty easy.

Let's go with the example code:

import com.google.analytics.AnalyticsTracker;
import com.google.analytics.GATracker;

const TRACKING_CODE:String = "UA-XXXXXXX-X";
const MODE:String = "AS3";
const DEBUG:Boolean = false;

var GAtracker:AnalyticsTracker;
var referrer:String;

GAtracker = new GATracker(this, TRACKING_CODE, MODE, DEBUG);

if (ExternalInterface.available)
{
    try
    {
        referrer = ExternalInterface.call("window.location.href.toString");
        if (referrer && referrer.indexOf("file:/") != -1) referrer = "localhost";
        if (!referrer) referrer = "unknown";    
    }
    catch (error:SecurityError)
    {
        trace("A SecurityError occurred: " + error.message);
    }
    catch (error:Error)
    {
        trace("An Error occurred: " + error.message);
    }

} else {
    trace("External interface is not available.");
}

As we don't use any Javascript we have to set the tracking code in AS3 along with the MODE set as AS3. Also, there's built-in debugger in the GATracker library which might be actually pretty handy.

Castles in the sand... box
Then we'll try to get the referrer URL – it's important to set <param name="allowscriptaccess" value="always" /> on the <object> tag, and the attribute for the <embed> tag allowscriptaccess="always" in your embed code to allow ExternalInterface access Javascript, otherwise Flash Player will spit out this SecurityError: A SecurityError occurred: Error #2060: Security sandbox violation: ExternalInterface caller [your SWF's host address] cannot access [the URL of the site where your SWF is embedded on].
We can catch it (which we do) but ain't get no URL then.

Got ya, how do I get to track events?
Easy tiger! For example, if you want to track the event of user pressing the video play button to start playback, just fire up this line:

GAtracker.trackEvent("Video", "Play: " + referrer, fileURL);

Where fileURL is the video file URL.

And finally...
Make sure you deploy your SWF on the live site, nothing gets tracked when the SWF is tested in Flash IDE or the local file system as Google explains:

Currently, Flash tracking is available for any Flash content embedded in a web page. Tracking of data sent from Adobe Air, Shockwave, or via the Flash IDE (e.g. using Test Movie) is not supported at this time.

Oh, well.

Any example/demo?
Sure, try to embed the code below on your blog (i.e. as a private post) to see how it works.

<object width="400" height="300" type="application/x-shockwave-flash">
    <param name="movie" value="http://play.blog2t.net/files/event-tracking/tracker.swf" />
    <param name="allowscriptaccess" value="always" />
    <embed
        type="application/x-shockwave-flash"
        width="400" height="300"
        allowscriptaccess="always"
        src="http://play.blog2t.net/files/event-tracking/tracker.swf"
    />
</object>
06:27 PM | | 1 Comment | Tags: , , ,
June 03, 09

Assassinating jumpy htmlText hyperlinks

UPDATE: Read this great tutorial on styling the HTML text field

I read quite recently that people are still swearing Flash jumpy hyperlinks in htmlText with Anti-aliased for readability option turned on. What are they talking about? – here's the example.

Roll over and out the links in the left column and carefully observe what happens to the text. Pretty bad stuff.

Fortunately, there is a perfect workaround discovered by Jonnie Hallman aka Destroy Today, but not everybody knows about it yet. So, (to save the world), I wrote a simple TextField wrapper class that automatically deals with the issue, additionally it also fixes another annoying bug – selectable code>TextField scrolls one line on text select – when you click and drag down the mouse while selecting the text, the content scrolls vertically! (and it shouldn't as the text height is the same as the box height, so there's nothing to scroll)

The fixed TextField is presented in the right column – again, try rollover the links.

How does the fix work then?
Basically, after setting the htmlText property, store the height of the TextField, then set autoSize to NONE. That will lock it, so no no more jumping is happening. It is all done at one go by overriding htmlText setter method.

override public function set htmlText(value:String):void
{
    autoSize = TextFieldAutoSize.LEFT;
    super.htmlText = value;
    recordedHeight = height;
    autoSize = TextFieldAutoSize.NONE;
    // adding extra height will prevent "vertical scroll on text select" bug
    // the extra height value should be just bigger than the default leading
    height = recordedHeight + getTextFormat().leading + 1;
}

Additionally, we're adding few pixels extra the TextField default leading value plus 1 to the height there (discovered by Luke Sturgeon), it will prevent "vertical scroll on text select" bug described above.

How should I apply this fix to my broken TextField?
By creating a FixedTextField instance instead, exactly the same way as you would instantiate an ordinary TextField:

var textTF:FixedTextField = new FixedTextField();

Alternatively, you could clone an existing TextField by referencing to it:

var textFieldFixed:FixedTextField = new FixedTextField(someOtherTextFieldToClonePropertiesFrom);

In fact, you can even attach the TextField stored in the Flash IDE library (obviously wrapped up in a MovieClip symbol) – I wrote a separate blog post on that matter.

Download zipped demo including the FixedTextField class.

01:30 PM | | 22 Comments | Tags: , , , , ,

A TextField called Dolly

I love Flash IDE. Especially the way you can stylise the TextField – decide what glyphs to embed and what fonts to include, set line height (leading), kerning (letterSpacing), colours, etc. I really hate creating new TextFields from scratch, entirely in code.

Using Library to store a TextField
Unfortunately, we can't store a TextField directly as a Flash IDE library symbol, so we have to wrap it up in a MovieClip and reference to it as follows:

var textFieldNormal:TextField = new TextMC().textTF;

where TextMC is the library MovieClip symbol containing a TextField named textTF.

How to duplicate (clone) a TextField?
You can't do it natively in AS3. But you can do some magic instead – clone all (excluding read-only) properties of another TextField.

var description:XML = describeType(referenceTextField);

for each (var item:XML in description.accessor)  
{
    // clone passed textfield properties that are not read only  
    if (item.@access != "readonly") targetTextField[item.@name] = referenceTextField[item.@name];  
}

targetTextField.defaultTextFormat = referenceTextField.getTextFormat();  

TextFormat of the referenceTextField won't be cloned with the loop above as it's not accessible via getter/setter/public methods. You have to clone it manually and this is what the last line does. It's very handy when you want to change the default kerning (letterSpacing) in Flash IDE and you want it to be preserved in your cloned text field.

I wrote a FixedTextField class that does that (plus fixes a couple of other TextField problems) – download zipped demo including the class.

01:00 PM | | 0 Comments | Tags: , , , , ,
May 12, 09

Finding the missing child

How many times you've encountered that annoying error ArgumentError: Error #2025: The supplied DisplayObject must be a child of the caller message?

It often happens when you're trying to remove a non-existing child which you would swear you had put there. How to find what depth is resides at, quickly? A simple "trace display list" package-level function closure traceDL(displayListElement, depth | "filter string") to the rescue!

package
{
    import flash.display.DisplayObjectContainer;
    import flash.display.DisplayObject;

    public function traceDL(container:DisplayObjectContainer, options:* = undefined, indentString:String = "", depth:int = 0, childAt:int = 0):void
    {
        if (typeof options == "undefined") options = Number.POSITIVE_INFINITY;

        if (depth > options) return;

        const INDENT:String = "   ";
        var i:int = container.numChildren;

        while (i--)
        {
            var child:DisplayObject = container.getChildAt(i);
            var output:String = indentString + (childAt++) + ": " + child.name + " ➔ " + child;

            // debug alpha/visible properties
            output += "\t\talpha: " + child.alpha.toFixed(2) + "/" + child.visible;

            // debug x and y position
            output += ", @: (" + child.x + ", " + child.y + ")";

            // debug transform properties
            output += ", w: " + child.width + "px (" + child.scaleX.toFixed(2) + ")";
            output += ", h: " + child.height + "px (" + child.scaleY.toFixed(2) + ")"; 
            output += ", r: " + child.rotation.toFixed(1) + "°";

            if (typeof options == "number") trace(output);
                else if (typeof options == "string" && output.match(new RegExp(options, "gi")).length != 0)
                {
                    trace(output, "in", container.name, "➔", container);
                }

            if (child is DisplayObjectContainer) traceDL(DisplayObjectContainer(child), options, indentString + INDENT, depth + 1);
        }
    }
}

So, where is my child?
With traceDL(someDisplayObject) you can reveal the whole display list of any DisplayObject. It will traverse its display list and show you all the instance names and types of the children (with depths they reside on), so it's easy to find out which depth your object hides. Some additional parameters like alpha, visibility, x and y coordinates, width and height (with scaling), and rotation will be displayed as well to help debugging (if it's too much, you can comment some of them out in the class file).

It's also possible to cut the traversing depth by specifying a number of levels to show: traceDL(someDisplayObject, 0) – will only show the first children, traceDL(someDisplayObject, 1) – will show you first children of someDisplayObject and also first children of its children.

Too many children shows up, it's like needle in the haystack!
No worries, just use traceDL(someDisplayObject, "filter string") where filter string is anything you think your child may contain, either it's an object's name, class (like MovieClip) or even some parameters (x: 100). Just anything that you think will match your child characteristics (that is what's outputted when you won't pass a search string).

Remember that if you hadn't explicitly set a name for a DisplayObject (either in code or Flash IDE), Flash player by default will call it instance and will assign a sequential number to it. Giving your object a name will always make it easier to identify.

I want to add a display object at a certain depth to a Library Symbol, but don't know the depth...
Nothing more perfect! With traceDL() it's childishly easy to trace any Library Symbol structure and determine what depth to use with addChildAt to dynamically add a DisplayObject to the Symbol.

Everything has slowed down!
The traceDL()function is recursive (unless you limit the depth it will dig till the last display node is scanned), and will use up a lot of your processor time, so take extra care and always remember to disable it in production code.

Erm, any demo/example usage?
Sure, there.

var rectangle:MovieClip = new MovieClip();
rectangle.name = "table";

var circle:Shape = new Shape();
circle.x = 100;
circle.y = 200;
circle.rotation = 45;
circle.scaleY = 0.5;

var square:Sprite = new Sprite();
square.name = "square";
square.rotation = 45;
square.scaleX = 0.5;
square.scaleY = 2;

var someSprite:Sprite = new Sprite();

addChild(rectangle);
rectangle.addChild(circle);
rectangle.addChild(square);
addChild(someSprite);

traceDL(this);
//0: instance4 ➔ [object Sprite]        alpha: 1.00/true, @: (0, 0), w: 0px (1.00), h: 0px (1.00), r: 0.0°
//1: table ➔ [object MovieClip]     alpha: 1.00/true, @: (0, 0), w: 0px (1.00), h: 0px (1.00), r: 0.0°
//   0: square ➔ [object Sprite]        alpha: 1.00/true, @: (0, 0), w: 0px (0.50), h: 0px (2.00), r: 45.0°
//   1: instance2 ➔ [object Shape]      alpha: 1.00/true, @: (100, 200), w: 0px (1.00), h: 0px (0.50), r: 45.0°

traceDL(this, "square");
//   0: square ➔ [object Sprite]        alpha: 1.00/true, @: (0, 0), w: 0px (0.50), h: 0px (2.00), r: 45.0° in table ➔ [object MovieClip]

traceDL(this, 0);
//0: instance4 ➔ [object Sprite]        alpha: 1.00/true, @: (0, 0), w: 0px (1.00), h: 0px (1.00), r: 0.0°
//1: table ➔ [object MovieClip]     alpha: 1.00/true, @: (0, 0), w: 0px (1.00), h: 0px (1.00), r: 0.0°

traceDL(rectangle, 1);
//0: square ➔ [object Sprite]       alpha: 1.00/true, @: (0, 0), w: 0px (0.50), h: 0px (2.00), r: 45.0°
//1: instance2 ➔ [object Shape]     alpha: 1.00/true, @: (100, 200), w: 0px (1.00), h: 0px (0.50), r: 45.0°

traceDL(this, "clip");
//1: table ➔ [object MovieClip]     alpha: 1.00/true, @: (0, 0), w: 0px (1.00), h: 0px (1.00), r: 0.0° in root1 ➔ [object MainTimeline]

traceDL(this, "shape");
//   1: instance2 ➔ [object Shape]      alpha: 1.00/true, @: (100, 200), w: 0px (1.00), h: 0px (0.50), r: 45.0° in table ➔ [object MovieClip]

If you were comfortable with AS2 "any depth" before and now feel a bit lost in AS3 depth management, traceDL() will definitely help you to switch.

You got me, I want it now!
Download the class traceDL.as

12:11 AM | | 10 Comments | Tags: , , ,