ActionScript a:hover Event

The only (easy) way to do a:hover styles is to apply a styleSheet. You don't get an event though, you just get a style change from the regular <a> style to the a:hover style.

I was hoping to find a way to set the window status message like you'd get in HTML. And I did find a way, even though implementing it will mean another trip into the dark world of HTML-as-XML parsing.

If (that's a mighty big if) you know the character indexes for your link characters, you can use TextField.getTextFormat and the Enter_Frame event to determine whether the link is in one color state or the other. Now the only problem is...figuring out those character indexes. It doesn't take an ActionScript whiz to figure out that your TextField.htmlText won't match your TextField.text; depending on your tags (and your whitespace, whitespace settings, etc) finding those indexes could be a major pain ... worthy of another post.

ActionScript, XML, and Character Entities (solved)

Nothing's quite as fun as staying up until 2am hacking an entity encoding problem. We're committed to keeping this excitement all to ourselves (so you don't have to).

Technically, XML only supports &amp; &lt; &gt; &quot; &apos;. All the others are XHTML. Flash also supports &nbsp;. Flash doesn't natively understand anything else, but you can add the support yourself. You're on the wrong side of the spec though, unless you add some processing instructions to define these new-fangled entities. Flash can't understand those either, but they'll keep browsers (and other parsers that do) from choking—and hey, interoperability is supposed to be the whole point of XML, right?

Before we dive in, two thoughts:

  1. I hate CDATA. I can never remember the sequence, and that means I always screw it up. You have to go to all the trouble to stick it in there, and then parse for it on the other side. No thanks.
  2. Numeric entities are supported by XML, but who can remember those? I just want to enter em-dashes and curly quotes, and I want to be able to recognize that in my highly-readable-non-cdata-infested markup.
  3. It'd be nice if you're doing transformations on your HTML (a future article will explore an example), to be able to leverage the XML parser so you'll have access to e4x, XMLList, prettyPrinting, etc. Wrapping HTML in CDATA helps you load it and apply as-is, but doesn't get you around Flash's re-encoding of "orphaned" ampersands.

Download XmlUtil, CharacterEntity, and StringUtils

It isn't required for Flash, but you'll want to add a DTD, or an inline definition of your entities like this:

 
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE site [
<!ENTITY mdash "&#8212;">
]>

 

"site" is the top-level node of your document—this one happens to be from a Gaia Framework project I've been working on. In this example, we've added "mdash," so now we can use &mdash; in our content.

How do we use this entity-encoded content in Flash?

var copyContent = XmlUtil.getHTMLContent ( myXML.description)

copy_tf.htmlText = "<body>" + copyContent + "</body>"

The body tag's on there because I'm using a stylesheet (with body rules). I could have put it directly in my XML if I'd wanted to. This example assumes my document has a node <description> as a direct child of the document root.

As I encountered in this post, ActionScript will reENcode ampersands on entities it doesn't understand when you get XML content via toXMLString(). The getHTMLContent() function DEcodes all &amp; to & before continuing to replace the XHTML entity set. Not exactly elegant, but it's better than the 1st thing I had, and I'm totally open to suggestions.

 

Additional Reading:

HTML entities in XML

Entity Reference

ActionScript, XML, and Character Entities

See if you can make heads or tails out of this behavior. I couldn't.

var myXML:XML = <doc><foo>&mdash; &amp; &lt; &gt; &quot; &apos; &nbsp;</foo></doc>

trace (myXML) //                   &amp;mdash; &amp; &lt; &gt; " ' ᅠ
trace (myXML.toXMLString()) //     &amp;mdash; &amp; &lt; &gt; " ' ᅠ
trace (myXML.toString()) //        &amp;mdash; &amp; &lt; &gt; " ' ᅠ
trace ("--") //
trace (myXML.foo) //               &mdash; & < > " ' ᅠ
trace (myXML.foo.toXMLString()) // &amp;mdash; &amp; &lt; &gt; " ' ᅠ
trace (myXML.foo.toString()) //    &mdash; & < > " ' ᅠ

Why are single and double quotes (and nonbreaking spaces?!) DEcoded by toXMLString, but any entities (ampersands) Flash doesn't handle get (re)ENcoded?

What a nightmare! I'm no encoding expert (although I did drop some cheddar on O'Reilly's excellent "Fonts and Encodings," a 1000+ page sleep-aid I have had zero time to actually read), but is there a rationale here I can't see?

Still musing about what to do about it.

ActionScript API for the Longtail Player (aka JW)

Took me forever to figure out how to set Longtail Player content via ActionScript.

I tried some other stuff, but I couldn't get both the preview image and the flv video to work the way it would if you used the HTML-embed set-and-forget method.

//videoView is a reference to the JW Player instance on stage
videoView.config.image = img // path to img as String
videoView.config.file = flv // path to flv as String
videoView.sendEvent(ViewEvent.LOAD,videoView.config);

Flash and Loading External Fonts – Part 1

You may have heard this elsewhere and not believed it (I did ... didn't?). If you want your (loaded) fonts to work in (loaded) flash movies, those movies may not (bold because I mean it) refer to those fonts. Don'ts:

  • You may not put the field on stage, set it to the desired font, but (purposefully) turn off font embedding. Flash will assume this swf has no outlines for this font and refuse to look elsewhere.
  • You may not have a static textfield that points to a font you (also) wish to load dynamically. Flash will only embed those outlines used in the static text and refuse to look elsewhere.
  • You may not break these rules on your main timeline, in (exported) library symbols, or try to load fonts first, and then load a swf which breaks these rules. Fonts will be broken for those swfs that try.

There are other resources out there detailing a few techniques for embedding fonts via a loaded swf. I'm going to save my juice for talking about the joy that happens (doesn't happen) when you try to embed bold and italics (which works) and then use them (which probably won't).

Flash TextField <img> Part 2

I promised I'd show how to split html content into multiple textfields to get around the fact that Flash's TextFields go nuts when you actually try to use the <img> tag. I do try to keep my promises. This post -- QED.

var inlineHtml:String = "";
 
var nodes = myXML.description.children()
for each(var node in nodes)
{
	var html:String = getHTMLContent("<doc>"+node.toXMLString()+"</doc>")
 
	if (html != "")
	{
		//Is this a block level tag: P, OL, etc...
		if ( isBlockTag (html, ["P", "OL", "UL"]) )
		{
		  if (inlineHtml != "")
		  {
		  	addSubfield(inlineHtml, stylesAsset.style)
		  	inlineHtml = ""
		  }
		  addSubfield(html, stylesAsset.style)
		}
		else
		{
		  inlineHtml += html + " " //NOTE: is a final space a major problem even with collapseWhite?
		}
	}
}
 
//anything left over as non-block content?
if (inlineHtml != "")
{
  addSubfield(inlineHtml, stylesAsset.style)
  inlineHtml = ""
}		
 
//
//
//
 
private function isBlockTag(html:String, blockTags:Array) :Boolean
{
	//convert the html string to XML
	var xml:XML = new XML(html)
 
	var nodeName = xml.name()
 
	if (nodeName != null)
	{
		for (var i in blockTags)
		{
			if (blockTags[i].toLowerCase() == nodeName.localName.toLowerCase())
			{
				return true
			}
		}
	}
	return false
}

The first block assumes we've got an XML snippet with a description node. We're going to walk through all the children of that node, and if it's a block-level element like <p> or <ul> we'll pass it (along with a stylesheet) to a function called addSubfield which will create the TextField object, set the StyleSheet, and set the HTML (this, and performing the actual layout of stacking one field atop the other is left as an exercise for the reader).

I showed you the getHTMLContent function in a previous article. Wrapping the argument in <doc> tags ensures we get all the content regardless of whether the content is a single text node, multiple HTML nodes, or mixed-content. I've been doing this a lot in a few projects -- works great -- so far.

Skinning the Longtail Player (aka JW)

This site is quickly becoming (just as we planned it ... Mu-hu-ha-ha!) a repository documenting things that took ages to figure out, we know we'll need again, but not often enough to trust our (rapidly aging) memories. The fact that its public and you get to play along is a bonus. I say that because writing this stuff up takes time, especially to do so clearly, neatly, and accurately. I have four projects going on, but I'm taking a moment to save this before I loose it; though perhaps muddled, disheveled, and ... suspicious.

Documentation for the Jeroen Wijering video player (http://www.longtailvideo.com/) leaves something to be desired -- it's great if you want to use it as they describe. Either:
1) embed a video in an HTML page where the Flash document is (and nothing but) the video player.

2) embed a player (and perhaps its skin) by loading those components at runtime.

These are great approaches. What I'd like to do is embed my player and skin at compile time causing it to bake directly into the swf I'm distributing. As a bonus, I'd like to compile against swc files in both cases so that:

a) Compiles are faster.

b) There is one-and-only-one version of the ActionScript floating around my harddrive which is messy enough already thank you.

I know I can accomplish "b" without a swc by adding the jw project folder to my classpath, but then that's one more thing to grab a copy of when I'm sending source to clients. Which I will forget about, and they won't actually try compiling anything until six months later at which point my copy of the source has changed ... See? I need a swc and I have good reasons.

Step 1: Create the player.swc

Open the player.fla that comes with the jw source. In the publish settings, check off create swc. Compile. Grab player.swc and add it to the classpath in your build tool of choice.

Step 2: The skin. (I'll post mine here later, but you should understand the steps)

Make a new fla, grab a copy of the library from the original and drop it in the new Skin.fla. Edit the properties of the "player" symbol. Set it to export with a class that we'll use in a bit CustomPlayerSkin. Follow the previous instructions for exporting a swc and adding it to your classpath.

In your project, you'll want to do something like this:


package {
	import com.jeroenwijering.events.ModelEvent;
	import com.jeroenwijering.events.ViewEvent;
	import com.jeroenwijering.player.View;

	import flash.events.Event;

	import com.jeroenwijering.events.PlayerEvent;
	import com.jeroenwijering.player.Player;

	import flash.display.MovieClip;

	/**
	 * @author projects
	 */
	public class PlayerHarness extends MovieClip {
		private var jwPlayer : Player;
		private var skin : CustomPlayerSkin;

		public function PlayerHarness()
		{
			addEventListener (Event.ADDED_TO_STAGE, onAdded)
		}

		private function onAdded(event : Event) : void
		{
		  populateAssets()
		}

		private function populateAssets(e:Event=null)
		{
			//jwPlayer
			jwPlayer = new Player()
			//remove default skin
			jwPlayer.removeChild (jwPlayer.player)
			//skin
			skin = new CustomPlayerSkin()
			jwPlayer.addChild(skin)

			//set new skin
			jwPlayer.skin = skin
			jwPlayer.width = skin.width
			jwPlayer.height = skin.height

			jwPlayer.config.autostart = true
			jwPlayer.config.fullscreen = true
			jwPlayer.config.resizing = false

			//size and position
			jwPlayer.config.width = this.stage.stageWidth
			jwPlayer.config.height = this.stage.stageHeight
			//skin.x = 0
			//skin.y = 0

			jwPlayer.addEventListener(PlayerEvent.READY, playerReady);

			addChild(jwPlayer)
		}

		private function playerReady(evt:Event=null) {
			var view:View = evt.target.view;

			//LOAD AN FLV!
			view.sendEvent(ViewEvent.LOAD, {file:"phone.flv"});

			//ADD AN EVENT
			view.addModelListener(ModelEvent.TIME, onPlayerTimeUpdate)
		}

		private function onPlayerTimeUpdate (e:ModelEvent)
		{
			trace ("pos" + e.data.position)
		}
	}
}

Photoshop Action – Save Slice for Web

Rob noticed today that every time he re-opens his site design doc the save-for-web window forgets that he's set it to "single slice" and not "all slices." We fumbled for a solution for a bit before settling on this: an action that copies the current slice to a fresh document and then invokes save-for-web from there.

This works because, as long as there's no current selection (make sure there's no current selection), the copy command will copy the content of the currently selected slice. Nice, eh?

Save the downloaded file as /Users/YOURUSER/Library/Application Support/Adobe/Adobe Photoshop CS4/Presets/Actions/shovemedia Actions.atn

Open Photoshop, go to the Actions pallete, click the menu in the upper right corner and select Load Actions...

I made this on (a Mac) CS4 -- your mileage may vary. Shouldn't matter though -- it's at least as easy to just record the action yourself.

Photoshop Tip – Transform Selection

I'm not sure how I missed this -- it's been in Photoshop since at least CS2 and from some of my Google results, probably far earlier than that.

When we cut images for the web, we use the Marquee tool a lot, but it can be a real pain to hit your corners exactly on the first try -- sometimes the pixels you're using as a visual guide are off screen if you've zoomed in far enough to see what you're doing! The solution (of course) is Transform Selection which gives you scale, rotate, and skew control over your current selection just like Transform Layer without messing with the underlying pixels. Very handy for expanding and trimming those rectangular selections.

Access it via right-click (command-click on Mac) of the selection or the Selection menu.