Easy (and Readable) Extendscript to JSFL Workflow

Justin | ExtendScript,Flash,jsfl,tips | Sunday, December 14th, 2008

Even though the original Adobe Creative Suite applications (Photoshop, Illustrator, InDesign) and the original Macromedia Studio applications (Flash, Fireworks, Dreamweaver) don’t speak the same language, they’re both versions of javascript. You can send code from Extendscript (used in PS, AI, ID) as a string to another Adobe application’s native scripting language, like Flash’s JSFL scripting language. The process of creating these strings is much like sending JSFL from Actionscript, long code strings have to be created. String assignments don’t take kindly to hard returns, so to make this code readable, the string containing the script can be broken into lines by appending using the += operator, similar to this pseudocode:

var codeString = "function myFunction() {";
codeString += "var myVar = 10;";
codeString += "for(var i=0; i < myVar; i++){";
codeString += " var newVar = i;";
codeString += "}";
codeString += "}";
sendStringToOtherLanguage(codeString);

While this is more readable than one string of text, it’s still a pain to edit. All code highlighting is lost. On top of that, you’re debugging across languages, so it slows testing even more. While working on a script recently I discovered a workflow that allows me to maintain code readability while working across multiple Adobe applications.

The solution came to me after discovering the toSource() javascript method.

You can download the example script in its entirety here.

This can be run from any Extendscript-enabled application (Illustrator, InDesign, Photoshop, Soundbooth, After Effects, or Bridge) and sent to any other application available to BridgeTalk (Contribute, Fireworks, Acrobat, Dreamweaver, Flash, Acrobat, etc).

You can set the source application by putting the script in the appropriate directory and running the script directly from the application or use the Extendscript Toolkit and select the application name from the drop-down at the top.

The breakdown is as follows…

I set the target application. In this case I’m targeting JSFL in Flash:

var targAppName = "Flash";
var sourceApp = BridgeTalk.getDisplayName(BridgeTalk.appName);
var targApp = getTargetAppName(targAppName);
var targAppStatus = BridgeTalk.getStatus(targApp);
var layerName = "layer created from " + sourceApp;

The following function is referenced above. It uses the targAppName variable to search for any open version of that application. It checks more recent versions of the application first. So if you have Flash CS4 and Flash CS3 open, it will find and return Flash CS4.

function getTargetAppName(nameStr){
//assumes higher versions are listed last
nameStr = nameStr.toLowerCase();
for(var i=apps.length-1; i>0; i--) {
if(apps[i].toLowerCase().indexOf(nameStr) != -1) {
if(BridgeTalk.getStatus(apps[i]) == "IDLE"
|| BridgeTalk.getStatus(apps[i]) == "BUSY"
|| BridgeTalk.getStatus(apps[i]) == "PUMPING"
&& BridgeTalk.getStatus(apps[i]) != "ISNOTRUNNING"){
//this version of the app is open
return apps[i];
}
}
}
return nameStr;
}

The stringifyFunction() function below takes a reference to another function, converts that function’s contents to a string using the toSource() method. The string returned from toSource() is encapsulated in parentheses, so those are removed before the string is returned.

function stringifyFunction(func){
var str = func.toSource();
var rStr = str.substring(1, str.length-1) + "\r";
return rStr;
}

The runTargetFunction is where I reference my JSFL functions (just one in this example). Instead of typing out each line of the function and appending it to a string I can call my stringifyFunction() method and pass my JSFL function (listed further down). Then I append my call to said function as a string, newLayer() in this case, and send it to my target application (Flash) using BridgeTalk. I also have some error checking with alerts to make sure the application is running and available.

function runTargetFunction(layerName){
if(targAppStatus == "IDLE"){
var bt = new BridgeTalk();
bt.target = targApp;
var scriptStr = stringifyFunction(newLayer);
scriptStr += "newLayer('" + layerName + "');";
bt.body = scriptStr;
bt.send();
BridgeTalk.bringToFront(targApp);
}
else if (targAppStatus == "ISNOTRUNNING" || targAppStatus == "ISNOTINSTALLED"){
alert(targAppName + " must be running for this script to work."); }
else if(targAppStatus == "BUSY" ) {
alert(targAppName + " is currently busy. Check the application to make sure no dialog boxes are open."); }
else { alert("There was an error when trying to interface with the " + targAppName + " application."); }

}

Next I run the function:

runTargetFunction(layerName);

Below my Extendscript functions, I list my JSFL functions just as if they were Extendscript. The Extendscript Toolkit is pretty forgiving and doesn’t mind that I have functions in different javascript dialects, so long as I don’t call the function directly from Extendscript. This script below is JSFL code that creates a new layer in Flash. If you have Flash open, but don’t have a document open, the script will open a new Flash document before creating the layer.

function newLayer(layerName) {
if(fl.documents.length < 1) fl.createDocument();
var layer = fl.getDocumentDOM().getTimeline().addNewLayer(layerName);
}

The layer created in Flash will list the application that sent the code. So if you ran this script from Photoshop CS3, the layer would be titled, “layer created from Photoshop CS3.”

I purposely wrote this example so that it would be easy to use with other applications. If you want to target a different application, change the functions at the bottom to work in another application (like Dreamweaver or Fireworks) using the application’s native JS and update the targAppName at the top. Keep in mind that communicating to most of these applications will only work in CS3 and later (post Adobe and Macromedia merger).

You can then write as many functions as you like for your target application and they’ll still be readable (just make sure to add them and call them in the string within runTargetFunction).

4 Comments »

  1. Very nice trick! That’s SO much easier than concatenating strings! I’ve linked to this article from one of my own articles on a related topic : Cross-scripting Adobe Illustrator and Flash.

    Comment by Jonathan Field — May 14, 2009 @ 9:47 am

  2. Thanks for linking to the article, Jonathan. Glad you found the information helpful!

    Comment by Justin — May 14, 2009 @ 5:34 pm

  3. Hi,

    Your stringify function is very smart.

    BTW, did you know you don’t need to concat your string code line by line with += ?

    Using backslash can do the trick too.

    var myString = “first line \
    second line \
    third line”
    But the problem with that method is that you loose code highlighting and it’s all about a string and function calls won’t work.

    Loic

    Comment by Loic — May 1, 2010 @ 9:51 pm

  4. Thanks for the comment, Loic! That’s a good point about the backslash. I should certainly use that more often.

    Comment by Justin — May 2, 2010 @ 2:56 am

RSS feed for comments on this post. TrackBack URI

Leave a comment

You may use the following html tags: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong> .


9 × = twenty seven

Subscribe without commenting