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:
htmlToFlash("<b>G'day <i>mate</b>, this is</i> pretty <b><i>cool</i></b> hey!");
That will allow you to set the value and format of a static text field with a result like this:
Here’s the function:
// This function removes the <b> and <i> 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, "<b>", "</b>");
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 <b> tags this iteration so they have all been replaced
replacedAllBolds = true;
}
}
// Replace the italic tags
var replacedAllItalics = false;
while (!replacedAllItalics) {
replaceResult = replaceNextTag(string, "<i>", "</i>");
if (replaceResult.tagStartIndex > -1) {
// Update indexes for bold tags after this italic tag
textAttrCollection = reduceIndexesGreaterThan(replaceResult.tagStartIndex, "<i>".length, textAttrCollection);
textAttrCollection = reduceIndexesGreaterThan(replaceResult.tagEndIndex, "</i>".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 <i> 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<textAttrCollection.length; i++) {
if (textAttrCollection[i].startIndex > theIndex) {
textAttrCollection[i].startIndex -= adjustment;
}
if (textAttrCollection[i].endIndex > theIndex) {
textAttrCollection[i].endIndex -= adjustment;
}
}
return textAttrCollection;
}
And here’s how you call it:
// Define the HTML string
var htmlString = "<b>G'day <i>mate</b>, this is</i> pretty <b><i>cool</i></b> 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);
}








