Thursday, June 04, 2009

Targeting Flash Player 10 for AIR projects with Flex 3.3

I got a project passed to me from Fraser and it was in AIR and Flex 3.3 for Flash Player 10. For some reason he could compile it but I was getting errors. This is after setting up my Eclipse (with FlexBuilder 3 plugin) for Flex SDK 3.3

Eventually it was determined that I needed to upgrade my FlexBuilder 3 plugin (not to 4!), to a slightly higher minor version because 3.02 introduces support for Flash 10. So, I launched my Adobe Updater application (you can search for it on Spotlight on a mac) and sure enough, immediately it told me there is an update for FlexBuilder. I ran that and my project was then able to compile. Hope this helps someone. Thanks for the frantic Googling Fraser!

Links:
http://www.adobe.com/devnet/flex/articles/sdk32_fb302.html

http://www.adobe.com/devnet/flex/articles/sdk32_fb302.html

Cheers,

Andrew

Wednesday, September 17, 2008

Elegant Code Snippets in blogger.

This might not be Actionscript/flash related but I thought I would explain how we got our code snippets to look so nice.

Searching the internet I ran into syntaxhighlighter which is a plugin that allows you to create nicer code snippets, unfortunately there is no style for Actionscript yet, but the Java highlighting style works quite nicely for AS snippets.

Quick instructions.

1. download the rar file from the google.code page (link above).
2. Unrar and upload to your own server under /syntaxhighlighter/
3. Add this to your blog template, just before the closing </body> tag.

<!-- START OF SyntaxHighlighter -->
<link href='http://www.YOUR_SITE.com/syntaxhighlighter/SyntaxHighlighter.css' rel='stylesheet' type='text/css'/>
<script src='http://www.YOUR_SITE.com/syntaxhighlighter/shCore.js' type='text/javascript'></script>
<script src='http://www.YOUR_SITE.com/syntaxhighlighter/shBrushCpp.js' type='text/javascript'></script>

<script src='http://www.YOUR_SITE.com/syntaxhighlighter/shBrushCSharp.js' type='text/javascript'></script>
<script src='http://www.YOUR_SITE.com/syntaxhighlighter/shBrushCss.js' type='text/javascript'></script>
<script src='http://www.YOUR_SITE.com/syntaxhighlighter/shBrushDelphi.js' type='text/javascript'></script>
<script src='http://www.YOUR_SITE.com/syntaxhighlighter/shBrushJava.js' type='text/javascript'></script>
<script src='http://www.YOUR_SITE.com/syntaxhighlighter/shBrushJScript.js' type='text/javascript'></script>
<script src='http://www.YOUR_SITE.com/syntaxhighlighter/shBrushPhp.js' type='text/javascript'></script>
<script src='http://www.YOUR_SITE.com/syntaxhighlighter/shBrushPython.js' type='text/javascript'></script>
<script src='http://www.YOUR_SITE.com/syntaxhighlighter/shBrushRuby.js' type='text/javascript'></script>
<script src='http://www.YOUR_SITE.com/syntaxhighlighter/shBrushSql.js' type='text/javascript'></script>

<script src='http://www.YOUR_SITE.com/syntaxhighlighter/shBrushVb.js' type='text/javascript'></script>
<script src='http://www.YOUR_SITE.com/syntaxhighlighter/shBrushXml.js' type='text/javascript'></script>
<script class='javascript'>
//<![CDATA[
function FindTagsByName(container, name, Tag)
{
var elements = document.getElementsByTagName(Tag);
for (var i = 0; i < elements.length; i++)
{
if (elements[i].getAttribute("name") == name)
{
container.push(elements[i]);
}
}
}

var elements = [];
FindTagsByName(elements, "code", "pre");
FindTagsByName(elements, "code", "textarea");

for(var i=0; i < elements.length; i++) {
if(elements[i].nodeName.toUpperCase() == "TEXTAREA") {
var childNode = elements[i].childNodes[0];
var newNode = document.createTextNode(childNode.nodeValue.replace(/<br\s*\/?>/gi,'\n'));
elements[i].replaceChild(newNode, childNode);
}
else if(elements[i].nodeName.toUpperCase() == "PRE") {
brs = elements[i].getElementsByTagName("br");
for(var j = 0, brLength = brs.length; j < brLength; j++) {
var newNode = document.createTextNode("\n");
elements[i].replaceChild(newNode, brs[0]);
}
}
}

//clipboard does not work well, no line breaks
//dp.SyntaxHighlighter.ClipboardSwf = "http://www.YOUR_SITE.com/syntaxhighlighter/clipboard.swf";
dp.SyntaxHighlighter.HighlightAll("code");
//]]>
</script>
<!-- END OF SyntaxHighlighter -->

4. Replace www.YOUR_SITE.com for your actual URL.
5. Post something following the syntaxhighlighter usage
6. You are Done.

Cheers.

FlexBuilder bug found today with event metadata

Found out the hard way today that you can't use camel case (as you should be able to) in event metadata in AS3 / MXML classes.

Here's my event class:

package ca.abcd.button
{
import flash.events.Event;

public class MyButtonEvent extends Event
{
public static const CLICK:String = "clickMyButton";
public function PlaylistEvent(type:String, bubbles:Boolean=true, cancelable:Boolean=false)
{
super(type, bubbles, cancelable);
}
}
}


Here's a snippet of a class that I want event metadata for. You should be able to define events that get dispatched from the class like this:

[Event(name="
clickMyButton", type="ca.abcd.button.MyButtonEvent")]
public class Playlist {

....


but when code hinting, for addEventListener, when you expect to see MyButtonEvent.CLICK in the list of event options, you get MyButtonEvent.CLICK_MY_BUTTON. What you'll find is you get underscores replacing any place in the event's string value where camel case is used.

Known Bug
Turns out it's a known FlexBuilder parsing bug and you can work around it by not using camel case and using underscores instead to separate words in your event value. You'll see I added a note in the bug report that it's still a problem in FlexBuilder 3.1. Hopefully they fix this soon, but otherwise, using underscores, no biggie, just a big of a waste of time, grr.


As a side note, if you're wondering what the problem is with just doing this inside of MyButtonEvent:

public static const CLICK:String = "click"

The reason is that MouseEvent.CLICK is also defined as "click" and so you'll get a conflict, catching events you don't mean to. The workaround has to then be in MyButtonEvent,

public static const CLICK_MY_BUTTON:String = "clickMyButton"

or

public static const CLICK_MY_BUTTON:String = "click_my_button"


or

public static const CLICK_MY_BUTTON:String = "CLICK_MY_BUTTON"

Saturday, September 13, 2008

Using namespaces to implement the state design pattern

I was reading up on the namespaces feature of AS3, trying to imagine a use for them (outside of the XML context), and decided that they would be useful for implementing the state design pattern. For that design pattern I used to use external state classes. Now, unless my various implementations for an API require a lot of code, it's cleaner to put the various implementations as same-named functions in the same class, differentiated with namespaces. Here's the classic gumball machine example that illustrates the state pattern that I implemented with the help of namespaces.


TryGumballMachine.as

package {

import ca.abcd.gumballmachine.GumballMachineModel;

public class TryGumballMachine extends Sprite
{
public function TryGumballMachine()
{
var gumballMachine:GumballMachine = new GumballMachine();
gumballMachine.turnHandle();
gumballMachine.insertCoin();
gumballMachine.insertCoin();
gumballMachine.turnHandle();
}
}
}


The above will trace out the following:

Can't turn handle, you must insert a coin first!
One coin inserted. Turn the handle to get a gumball.
You can't put a coin in the machine right now because there's already one there. Turn the handle then add another coin.
I hereby give you a gumball, don't forget to brush your teeth!



GumballMachine.as

package ca.abcd.gumballmachine
{
public class GumballMachine
{
private namespace hasCoin;
private namespace noCoin;

use namespace noCoin;
private var _state:Namespace;

public function GumballMachine()
{
_state = noCoin;
}

public function turnHandle():void {
_state::turnHandle();
}

public function insertCoin():void {
_state::insertCoin();
}

noCoin function turnHandle():void {
trace("Can't turn handle, you must insert a coin first!");
}

hasCoin function turnHandle():void {
trace("I hereby give you a gumball, don't forget to brush your teeth!");
}

noCoin function insertCoin():void {
_state = hasCoin;
trace("One coin inserted. Turn the handle to get a gumball.");
}

hasCoin function insertCoin():void {
trace("You can't put a coin in the machine right now because there's already one there. Turn the handle then add another coin.");
}
}
}

Labels:

Tuesday, September 09, 2008

Open source Flash player called Gnash

Did you know there is an open source version of the Flash Player? I was doing some self interest research into the One Laptop Per Child (OLPC) laptop to see what it is like (I'm working getting an emulated version running on my Mac with VMware Fusion). I was curious to know basically, if I wanted to create educational/useful Flash content for people in developing countries would they be able to view it. The short answer appears to be yes. The longer answer is that they'll have to know how to download the Flash Player like in the old days. However, out of the box, some of your Flash may work fine anyway.

Because of licensing restrictions on the Red Hat Linux operating system that ships with OLPC, the actual Flash Player could not be used (or would have been cost prohibitive I guess of their goal of one or two hundred dollars per unit). Instead they ship with Gnash, which purports to play Flash 7 content pretty well and even some Flash 8 and 9 stuff.

Labels:

Friday, July 04, 2008

Google and Yahoo indexing Flash (SWF) content like never before. Finally!

To me this has huge implications and to some degree (time will tell), will get a monkey off our backs in terms of how flash is great but not great for SEO. Of course we had workarounds but I'm not sure that they were 100% effective and the added time/cost overhead. Not anymore!

click here for the article

Labels:

Thursday, May 08, 2008

Creating your AIR installer without impurities

I recently had an issue with my Adobe AIR project where the application installer I created (myInstaller.air) stopped working. I could create it, but it complained of corruption when I tried to run it. There was really no explanation why.

Here's what I discovered after some trial and error to weed out the problem:

I had downloaded a third party skin for the app from scalenine.com, which consisted of a swf and some css. The skin itself was fine, I could see it working in the test environment. In addition to the skin files, it also came with a hyperlink file that linked back to the author's Website. I believe it was something like MyWebsite.webloc. The problem I was experiencing was due to the installer including the .webloc file. When you generate your .air installer file from FlexBuilder 3, you choose which files should be contained in it. I had selected the root folder of the skin package.

Obviously a .webloc file will have nothing to do with Adobe AIR, but surprisingly it acted as a pathogen rather than a benign file.

So, if this happens to you, look for files that have nothing to do with your app, and don't include them in your export!

Labels:

Tuesday, March 04, 2008

relaying non-bubbling events

Something I thought should work but had trouble figuring out was just revealed to me today. Say i have a couple of 'nested' objects, but they're NOT display objects, and I want to 'bubble' the event up through the parents. Of course conceptually real bubbling will work in this situation if the parents were display objects, but if they're not it's still possible without too much code.

Here's the example of what we want to work, made up of classes MyClassA and MyClassB and event class MyEvent:

class MyClassA {
private var b:MyClassB;

public function MyClassA():void{
this.b = new MyClassB();
this.addEventListener(MyEvent.START, relayEvent);
this.addEventListener(MyEvent.PROGRESS, relayEvent);
this.addEventListener(MyEvent.FINISH, relayEvent);
}

public function start():void {
this.b.start();
}

private function relayEvent(event:MyEvent):void {
//relay same event.
dispatchEvent(event);
}
}


class MyClassB {
public function MyClassB():void{

}

public function start():void {
var event:MyEvent = new MyEvent(MyEvent.START);

dispatchEvent(event);
}
}

class MyEvent extends Event{
public const START:String = "start";
public const PROGRESS:String = "progress";
public const FINISH:String = "finish";

public class MyEvent(type:String, bubbles:Boolean = false, cancelable:Boolean = false ) {
super(type,bubbles, cancelable);
}
}

In the above we're trying to expose several of MyClassB's events (which could be of different event class types) to observers of MyClassA, whom we do not want to have to know about MyClassB.

In your observing class picture the following:

class MyObserver {
public function MyObserver():void {
var myObjA:MyClassA = new MyClassA();
myObjA.addEventListener(MyEvent.START, onStart);
myObjA.start();
}

private function onStart(event:MyEvent):void {
//do something useful
}
}

The problem is, that when you run this you'll get a type coersion error at MyObserver#onStart in relayEvent. This is because the MyEvent object in MyClassA#relayEvent gets changed from type MyEvent to Event.

The solution is to create clone methods in each of your custom event classes that duplicate the class and return the same type. Behind the scenes, the Flash Player is calling clone, but since it doesnt' exist, it calls the super (Event#clone()) which returns its own type, Event. Here's my custom event class again with the clone method:

class MyEvent extends Event{
public const START:String = "start";
public const PROGRESS:String = "progress";
public const FINISH:String = "finish";

public class MyEvent(type:String, bubbles:Boolean = false, cancelable:Boolean = false ) {
super(type,bubbles, cancelable);
}

public function clone():MyEvent {
var clone:MyEvent = new MyEvent(this.type, this.bubbles, this.cancelable);
return clone;
}
}