This is foremost a note-to-self. That enticing entry in the ActionScript API for TextField events that looks like you ought to be able to detect changes to the text or htmlText property? That's not how it works. It's for TextFieldType.INPUT fields only. As the user makes changes, the event will fire, but for everything else, you're on your own. I keep managing to forget this and architecting things inappropriately as a result.
There are several reasons you might want to know where one TextFormat ends and another begins. If your application supports any HTML authoring, you'll likely need something like this, even if your HTML support is fairly basic. I've been working on a font loading management utility, so I needed something to detect which portions of the text use which font. Once I have this, I can use Font.hasGlyphs to determine whether I need to load a deeper subset of glyphs or can get away with a more minimal unicode range. (You can quit drooling. That code will be posted here when it's ready.)
Doing this requires two classes. The first determines whether two TextFormat instances are equal.
-
package {
-
public class TextFormatUtil {
-
public static var defaultPropertySet:Array /* of String property names */ = ['font', 'size', 'color', 'underline', 'bold', 'italic', 'url'];
-
{
-
if (propertySet == null)
-
{
-
propertySet = defaultPropertySet
-
}
-
{
-
var prop = propertySet[i]
-
//trace (i + " " + prop + " " + f1[prop] + " " + f2[prop])
-
if (f1[prop] != f2[prop])
-
{
-
match = false
-
break
-
}
-
}
-
return match
-
}
-
}
-
}
Easy enough. Now, you could just walk through each character in the TextField one at a time testing for TextFormat equality. But that's really inefficient. Instead, I implemented a binary search.
-
package {
-
public class TextFormatMapper {
-
public function TextFormatMapper() {
-
}
-
{
-
textField = tf
-
}
-
mapTextFormat()
-
}
-
public function mapTextFormat()
-
{
-
var startIndex = 0
-
while (startIndex < textField.text.length)
-
{
-
while (lo < hi) {
-
if ( TextFormatUtil.equals (format1,format2, TextFormatUtil.fontOnlyPropertySet) )
-
{
-
//try a larger span if possible
-
lo = mid //+ 1
-
}
-
else
-
{
-
//try a smaller span if possible
-
hi = mid - 1
-
}
-
}
-
charsInTextFormatAtIndex.push ( charsInTextFormat )
-
startIndex = hi
-
}
-
}
-
return charsInTextFormatAtIndex;
-
}
-
}
-
}
To use it, you create a new TextFormatMapper and pass your TextField to the setTextField method. You can see I'm automatically calling the mapTextFormat method when the Event.CHANGE fires on the TextField. For testing, I hardcoded it to only test against the font property, but you can see how to easily change this to another property set on line 44. Once it's finished, you can call getCharsInTextFormatAtIndex to get an array containing the length (in characters) of each distinct TextFormat (in this case, distinct font) encountered in the TextField.
This class will change a bit as I move forward with it, but I was really pleased to get this far.
I'm going to keep my eyes open for an actual explanation for this behavior, but I thought I'd share a problem (and solution?) I ran into with mod_rewrite.
I was going about my business, doing the classic mapping of http://mydomain/subdir/var1/var2 to http://mydomain/subdir/index.php?id=var1&id=var2
I had two rules, one to handle a single variable, and another to handle two variables. The idea, of course, being to drill down to a deep link. It was working great for the single variable rule, as long as I didn't include a trailing slash. Even though my rewrite regular expression clearly stated that the trailing slash was optional. To taunt me even more, if I changed the rewrite to a redirect, it worked fine.
As it turned out, the page in question contains a frameset (I know, I know) and the frame source parameters were relative paths. Even though the page source URL had been re-written, the frame source URLs were attempting to load pages that weren't there. Changed the paths to absolute and everything works great.
Just something else to look at if you're having trouble with mod_rewrite, especially if some rules appear to work, and others don't. The frameset situation is going to be pretty rare, but the same problem might manifest with missing images, style-sheets, etc.
Here's a simple Smarty modifier that will format an integer that represents the number of bytes in a file as a human readable string.
Usage:
{$fileSizeInBytes|file_size}
Example:
{assign var=fileSizeInBytes value=10485760}
{$fileSizeInBytes|file_size}
{assign var=fileSizeInBytes value= 768000}
{$fileSizeInBytes|file_size}
{assign var=fileSizeInBytes value=303}
{$fileSizeInBytes|file_size}
Output:
10 MB
750 Kb
303 bytes
<?php /** * Smarty plugin * @package Smarty * @subpackage plugins */ /** * Smarty file_size modifier plugin * * Type: modifier<br> * Name: file_size<br> * Purpose: format file size represented in bytes into a human readable string<br> * Input:<br> * - bytes: input bytes integer * @author Rob Ruchte <rob at thirdpartylabs dot com> * @param integer * @return string */ function smarty_modifier_file_size($bytes=0) { $mb = 1024*1024; if ($bytes > $mb) { $output = sprintf ("%01.2f",$bytes/$mb) . " MB"; } elseif ( $bytes >= 1024 ) { $output = sprintf ("%01.0f",$bytes/1024) . " Kb"; } else { $output = $bytes . " bytes"; } return $output; } /* vim: set expandtab: */ ?>
Most professional-grade fonts don't register the bold & italic outlines with the same postscript name. For example, "Berthold Akzidenz Grotesk BE", "Berthold Akzidenz Grotesk BE Bold", "Berthold Akzidenz Grotesk BE Italic" So to get these to work, you've got to either use stylesheets or explicitly set the text to use the correct face.
I updated my portfolio recently, and I didn't have time to implement a new shell (unfortunately) or code the whole thing over (as much fun as that is). Nor did I feel like tracking down a bug that kept me from publishing the swf to target anything above Flash Player version 6. Yeah, 6.
There wasn't any StyleSheets class back then. Add to this my crazy text-layout-in-a-circle routine, and adding bold, italic, and link support is an absolute nightmare. But I did it, and now I can show you how.
1st, parse the html as xml and replace the <b> and <i> tags with <font face=""> expressions that will actually target the correct font. I actually wrote a class that lets you map any tag to any other tag.
Next, stick all the html in a temporary textfield and map out where all the TextFormat changes occur. I wrote another class that can compare two TextFormats for equivalence.
My text-in-a-circle code adds words one-at-a-time, effectively creating a custom word-wrap routine. However, I had trouble when I attempted to use a single TextField, so each row uses a separate one. If you're up to something like this, you have to keep track of how many characters you've already used in previous fields. At first, I was getting lots of off-by-one errors and found a carriage-return character (character code 13) kept slipping into my input. Funny, I didn't put that there...
Flash also doesn't do a great job rendering things like <b>word</b> <b>second word</b>. It likes to collapse that and the space disappears. I swapped the spaces for non-breaking-spaces ( ) Feels dirty, but gets the job done.
And remember all those posts about entity problems with HTML coming from XML? The big reason for this undertaking was so I could support links in my portfolio project descriptions. When I linked to google maps, guess what happened to the query parameter ampersands in the href ...
The code is so crufty, even I won't post it. Drop me a line and I'll send you a private peek.
I love OS X. For me, it's the ideal development platform. I primarily build web applications that run on the LAMP stack, so having a POSIX compliant UNIX like OS for my daily driver is extremely convenient. My development environment works just like the environments I deploy to, so I don't have to worry very much about platform inconsistencies. OS X is the perfect balance of elegance and power. I only wish Finder didn't suck so hard.
There are some actions I need to perform on files on a regular basis that are not supported in Finder. For example, recursively deleting files in a tree of directories, but leaving the directory structure intact. I have a code generation tool that scaffolds out a boatload of PHP classes based on MySQL database. During heavy development, I need to blow away the files on a regular basis without deleting the output directories. Finder simply cannot do this. Luckily Apple has provided a way to add Automator actions to the context menu in Finder. I put together a simple workflow to recursively delete files in a tree of folders, here is is for your convenience:
DeleteFolderContents.zip - MD5: 5d0ad832e9e998dbefd70d49f2a07b52
Unzip, and copy the workflow file to Library/Workflow/Applications/Finder/ in your home directory. If all has gone well, you should have a new option in the context menu when you right-click a folder in Finder:

Automator Workflow As Finder Plugin
Another Finder deficiency that was bugging Jon recently is the inability to copy the filesystem path of a folder in Finder to your clipboard. I put together a little workflow that does just that:
CopyPathToClipboard.zip - MD5: b64558cf3afb7aecdec63faccb5986ba
Follow the same steps outlined above to copy the workflow to the proper location.
Note that these workflows are compatible with OS X version 10.5, and will not run in Automator on 10.4 or previous systems.
Jon and I delivered a presentation about SEO to the NCSU Web Developer Group this afternoon. If you were in attendance, thanks very much for coming to see us, we hope you found it informative. We talked about a lot of useful online resources during the presentation, all of the links are in the slide deck that we've posted for your convenience. You can download a PDF of the slides here, or view them on SlideShare.
Please feel free to post comments and questions here.
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.
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 & < > " '. All the others are XHTML. Flash also supports . 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:
- 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.
- 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.
-
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 "—">
]>
"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 — 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 & 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:
See if you can make heads or tails out of this behavior. I couldn't.
var myXML:XML = <doc><foo>— & < > " ' </foo></doc>
trace (myXML) // &mdash; & < > " ' ᅠ
trace (myXML.toXMLString()) // &mdash; & < > " ' ᅠ
trace (myXML.toString()) // &mdash; & < > " ' ᅠ
trace ("--") //
trace (myXML.foo) // — & < > " ' ᅠ
trace (myXML.foo.toXMLString()) // &mdash; & < > " ' ᅠ
trace (myXML.foo.toString()) // — & < > " ' ᅠ
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.



