ActionScript, E4X, and White Space

english mobile

I've been reading XML into ActionScript 3 for many years, and I still haven't solved all the issues with it. Given that my searches for solutions to these issues turn up so little, I decided to share the solutions I have found, especially when it comes to white space.

I think at least part of the reason that these problems are so hard to solve is that the XML documentation for many of the properties and methods we need to use is frustratingly circular. So let me start by laying out what some of these do in practice.

ignoreWhiteSpace

I think ignoreWhiteSpace is the property we first try to use to correct some of the issues with E4X. This property will, in fact, fix issues that are caused by having "extra" children that you weren't expecting to be parsing when you use normal formatting, like new lines and tabs, in your XML. However, it then causes other white spaces that might have been pretty important to disappear.

An alternative to using ignoreWhiteSpace is to use xmlVar.elements() instead of xmlVar.children(). This works relatively well unless you need to access something that's not an element, like a comment, or you need to pass the XML to Adobe code (like new DataProvider()) that for some reason is not equipped to handle the vagaries of their own E4X implementation.

prettyPrinting

We're all familiar with the problem where the default XML parsing adds even more formatting than we included in the original XML file, so that

<root>
 <p>The quick brown fox jumped over the
 <b>lazy</b> dog.</p>
</root>
becomes
<root>
 <p>The quick brown fox jumped over the 
  <b>lazy</b> 
 dog.</p>
</root>

I'm sure there are some versions of Flash where setting prettyPrinting to false fixes this problem, but in the version of the Flash player we target, setting this property has no effect. The good news is that TextField, the Flash Label component, and the Flex MX Label component support the condenseWhite property, which gets rid of this issue for text you want to display. I guess if you're using a spark label, the skin will need to use something that supports condenseWhite if you need this.

Wrap in CDATA

One of the reasons XML parsing is so thorny is that we're using the XML to store external content, often HTML-formatted. CDATA does work to preserve HTML formatting, but it has its own issues. One issue is that there's no real documentation about how to get the text out of CDATA.

Funny story--I was once on a Flex project where it turned out I needed to wrap the entire XML package I was building in CDATA. Try though I might, I couldn't find anything that told me how to get at the "stuff" inside the CDATA. With deadline fast approaching, I "hacked" it by giving the text to a Spark label (which did know how to do this), then reading the text out again and casting it to XML.

Some months later, I ultimately discovered that String(myNode) will retrieve the contents of a node, whether its contents are wrapped in CDATA or not. Unless the contents are not wrapped in CDATA but contain HTML formatting anyway. This is something that is quite likely to happen on my team, as the people who produce the XML see no reason they shoulld start wrapping everything in CDATA just because we're using ActionScript 3 now.

In addition, even when things are wrapped in CDATA when they should be, you have to dump your text in one line into CDATA because it faithfully reproduces all those \n's and \t's, and you wind up with a bunch of junk nodes if you have to cast the contents back to XML. This results in XML that isn't readable, and readability is one reason to use XML in the first place.

hasComplexContent

So, if you're getting XML and you know that sometimes certain nodes will contain HTML and sometimes that HTML will be wrapped in CDATA and sometimes it won't, what do you do? One thing you can do is create a function that checks for hasComplexContent and calls toString() if it returns false and children().toXMLString() if it returns true.

This works well, but one issue with it is you have to have access to this function wherever you're parsing XML. And since you can't really extend the XML Class, due to its dependence on static methods and properties, that means you either need to use a static method yourself (not my favorite thing) or you need to violate DRY, unless your application lends itself to only parsing XML nodes in exactly one place.

normalize()

This is probably my favorite method, because it gets rid of all the weird child nodes that contain "\n\t\t". This means that I can now parse formatted XML and not have to allow for weird extra junk in there. More importantly, I can pass that XML to "dumber" code, such as the Flash DataProvider constructor, and it works. It also does not take out the spaces around inline HTML tags, so you can normalize() the XML to make for easy parsing that you should get from ignoreWhiteSpace (but don't, due to the removed spaces), then you can call String(myXMLNode) on the node whether it has complex content or not, and it will work in a TextField with condenseWhite set to true. FTW!

Batch Import Images into Flash Symbols

english mobile

When I find a workflow I like, I sometimes assume that everyone has already discovered that workflow, and I'm late to the party. One of the nice things about participating in user forums is that sometimes that helps me realize that a favorite workflow isn't known to everyone. This is great, because then I have something to blog about!

Today, I want to talk about what is probably one of my all-time best productivity tricks in Flash. In a few minutes, I can:

  1. Import a series of pngs, jpgs, or whatever into my Flash movie
  2. Choose for each image to be wrapped in a symbol or not
  3. Have the symbols and images nicely organized into folders in the library, without any extra effort!
Here's how I do it, starting with a folder of images:

Step 1: Browse the folder in Bridge

First, I right-click on the folder of images and select "Browse in Adobe Bridge"
Once it opens, I select the images I want to use. For this post, I just selected everything in the directory, but Bridge has excellent features for finding just the images I want (that could be a blog post in itself). You can drag them around in Bridge to change the order, if you need to.

Step 2: Load the files into PhotoShop Layers

In the Bridge "Tools" menu, I select PhotoShop>Load Files into PhotoShop Layers.

This launches PhotoShop and does all the work to put the files into PhotoShop layers, in the order they were in in Bridge. Once that has finished running, save the result as a .psd.

Step 3: Create a Symbol in Flash to Import into

Whenever I have to create a new Symbol in Flash, I like to start with a rectangle that's the size I want the MovieClip to wind up, so the registration and transformation points fall in predictable places. So, I create a rectangle the size of my images and convert it to a Symbol (F8).

Step 4: Import the PSD

I usually use the keyboard shortcut to import to stage (Ctrl-R). The import dialogue gives me lots of options. For example, I can select all the images and choose to convert them to MovieClips.
I'll usually convert the images to MovieClips if I want to do timeline animation with them or if I want to apply a base Class to the MovieClip. I can also choose to import each to a different layer or to a different keyframe. Both options are useful, depending on the end result I need.

Once the psd has imported, the timeline looks like this.

Note that the frames will be the reverse order of the order they were in in Bridge. I usually select the frames and reverse the frames (there's a right-click menu for it, but I've set up a keyboard shortcut).

Step 5: Edit the Library Symbols

The MovieClips and Images are nicely organized, but usually there is more I want to do to them.
If I'm applying a Base Class, I might want to rename all the Symbols, for instance, to change ".png" to "_png." I like the Search and Replace extension for this.

Flash also allows you to edit more than one library symbol at a time, by selecting several symbols and clicking the Properties button (sorry, if there is a shortcut for this, I haven't found it).
From this dialogue, you could change all the MovieClips to Graphics, for example, or you could set a Base Class on all the MovieClips at once. Tip: if you set a Base Class, Export for ActionsScript will become checked and the drop-down will have "Yes" selected, and you don't have to touch it.

And that's it, my quick and easy workflow for importing a series of images to Flash.


FlashTips #6: Debug refresh issues in your swf

english mobile

Most of us know that Flash Builder makes it easy to debug a swf on a server by creating a custom debug configuration. Recently, I had a problem where I needed to be able to debug issues that happened when the browser window was refreshed.
Jeffry Houser, the brains behind Flextras.com, apparently had this very same problem, and resolved it by starting a new debugger session that points to about:blank and then connecting that to the swf after the window has been created.
That's a great solution, but the issue I was having was happening pretty much immediately after the window was refreshed--if I went through his steps, I'd "miss the window." So, after limping along for several hours, trying to figure out the problem without having the direct debug information, I finally figured out that the problem was amazingly simple:

  1. Create a new "refresh" debug configuration that doesn't point to a page with a swf in it (I didn't yet know that about:blank was an option).
  2. Start the original debug configuration, that points to the swf as normal.
  3. Get to the problem point in the application.
  4. Launch the "refresh" configuration.
  5. Refresh the window.
  6. Voilà, we are connected, from the very beginning of the bootstrapping process.
Enjoy!