How to insert rich text into a static text field with JSFL

By

Automating the Flash authoring environment with JSFL.

I avoid repetition, I can't stand redundancy and I get bored easily. So when I was assigned the task of developing 270 very similar Flash banners, I thought there has got to be a way to automate this. I was happy to discover that JavaScript Flash (JSFL) fit the bill perfectly.

This particular project had five different banner designs (skyscraper, rectangle, leaderboard, etc) and each design was to be published with fourteen different sets of images and four different call-to-action labels. I couldn't load the assets dynamically because the banners had to be standalone SWF files. I couldn't embed the assets with code because I was working with AS2. So the only way to do it was to automate the Flash authoring environment itself.

I'm going to explain one part of my approach in more detail - how I replaced the values of the text fields. Updating static text fields is easy enough with JSFL, but what about when part of that text is bold or italic? The Text.setTextAttr() method lets you apply formatting to subsets of the text, as long as you know the start index and end index of the text you want to format. Normally you don't know those indices off hand, so I wrote a function that lets you place bold and italic HTML tags in your string to mark their location. Calling the function looks like this:

[Javascript]
htmlToFlash("G'day mate, this is pretty cool hey!");
[/Javascript]

That will allow you to set the value and format of a static text field with a result like this:

Rich text in a static text field in Flash with JSFL

Here's the function:
[Javascript]
// This function removes the and tags from a string, and returns the string and an array of TextAttr objects that can be used to format the string in a static text field in Flash.
function htmlToFlash(string) {
var replaceResult;
var textAttrCollection = Array();

// Replace the bold tags
var replacedAllBolds = false;
while (!replacedAllBolds) {
replaceResult = replaceNextTag(string, "", "");
if (replaceResult.tagStartIndex > -1) {
// Push the attribute to the collection
textAttrCollection.push({"attrName" : "bold", "attrValue" : true, "startIndex" : replaceResult.tagStartIndex, "endIndex" : replaceResult.tagEndIndex});
// Update the string with the bold tag removed
string = replaceResult.string;
} else {
// replaceNextTag did not replace any tags this iteration so they have all been replaced
replacedAllBolds = true;
}
}

// Replace the italic tags
var replacedAllItalics = false;
while (!replacedAllItalics) {
replaceResult = replaceNextTag(string, "", "");
if (replaceResult.tagStartIndex > -1) {
// Update indexes for bold tags after this italic tag
textAttrCollection = reduceIndexesGreaterThan(replaceResult.tagStartIndex, "".length, textAttrCollection);
textAttrCollection = reduceIndexesGreaterThan(replaceResult.tagEndIndex, "
".length, textAttrCollection);
// Push the attribute to the collection
textAttrCollection.push({"attrName" : "italic", "attrValue" : true, "startIndex" : replaceResult.tagStartIndex, "endIndex" : replaceResult.tagEndIndex});
// Update the string with the italic tag removed
string = replaceResult.string;
} else {
// replaceNextTag did not replace any tags this iteration so they have all been replaced
replacedAllItalics = true;
}
}

var result = {"string" : string, "textAttrCollection" : textAttrCollection};
return result;
}

// This function removes an XML tag from a string, and returns the string and the character locations of the opening and closing tags
function replaceNextTag(string, tag, closeTag) {
var tagStartIndex = string.indexOf(tag);
if (tagStartIndex >- 1) {
// Chop out the opening tag
string = string.substring(0, tagStartIndex) + string.substring(tagStartIndex + tag.length);
var tagEndIndex = string.indexOf(closeTag, tagStartIndex);
// Chop out the closing tag
string = string.substring(0, tagEndIndex) + string.substring(tagEndIndex + closeTag.length);
}
var result = {"string" : string, "tagStartIndex" : tagStartIndex, "tagEndIndex" : tagEndIndex};
return result;
}

// When a tag is removed we need to adjust the character index for existing TextAttr objects which are positioned further in the string
function reduceIndexesGreaterThan(theIndex, adjustment, textAttrCollection) {
for (var i=0; i theIndex) {
textAttrCollection[i].startIndex -= adjustment;
}
if (textAttrCollection[i].endIndex > theIndex) {
textAttrCollection[i].endIndex -= adjustment;
}
}
return textAttrCollection;
}
[/Javascript]

And here's how you call it:
[Javascript]
// Define the HTML string
var htmlString = "G'day mate, this is pretty cool hey!";

// Convert the HTML string into a plain string and an array of TextAttr objects for formatting
var result = htmlToFlash(htmlString);

// Get the text field
var txtField = fl.getDocumentDOM().timelines[0].layers[0].frames[0].elements[0];

// Set the value of the text field
txtField.setTextString(result.string);

// Apply the TextAttr format objects to the text field
for each (var textAttr in result.textAttrCollection) {
txtField.setTextAttr(textAttr.attrName, textAttr.attrValue, textAttr.startIndex, textAttr.endIndex);
}
[/Javascript]

This entry was tagged with Flash. Bookmark the permalink.

Comments

  1. Thanks for the post - great JSFL script! I'm having a little issue with a similar (but simpler) script, which simply replaces text in a variety of objects in open Flash docs. The problem is that the textfields themselves update as a result of the setTextString() call, but the main timeline often will not update to show the new version until I close/reopen the document. I can even double-click to jump inside an object, see that its textfield has been updated, and then return to the root timeline to see the textfield still showing the old text. Publishing works fine (shows the updated text). Have you ever run in to this with setTextString()?

  2. Hey Momo,

    Thanks for the comment! That sounds like a strange problem you're running into - but strange is normal when it comes to JSFL.

    In the end does it really matter if the main timeline doesn't update? As long as the output is good I don't think bugs in the authoring environment are that important.

    Cheers,
    Keegan

  3. Impressive script! Would you know of an easy way to do the reverse of this script? That would REALLY help me out. I’ve got a ton of flash files (countable in hundreds) where I need to replace regular flash text fields with a component-based text field that will only accept html-style text for formatting. All my regular text fields are set to render as HTML to allow several properties within one text field(bold, italics, and URL), and so far I’ve only been able to replace the text fields without formatting :/. I’ve been unable to find a way to copy the text WITH formatting, that is then converted to HTML format. Is there a way to do this?

  4. By Keegan Street

    Hey Alex, Sorry but I haven't been working with Flash in over a year. I don't really know what's possible with JSFL these days. The only thing I can suggest is to have a look through these docs: http://help.adobe.com/en_US/flash/cs/extend/index.html and see if there is a way to create the text field you need with JSFL.

  5. Ah, too bad. Still, thanks for replying!

Leave a Reply