Avg. Rating 5.0

Problem

UndoModes in InDesign (CS4 and later) allow for multiple steps in a script to be saved as a single step in the undo stack. Using this capability in a CS SDK extension is not very straight-forward because you can't pass in ActionScript code to a doScript. doScript() accepts one of the three scripting languages which ActionScript is not one.

Solution

Use one of the native scripting languages as the argument of the doScript(). There are many ways of approaching this as described below.

Detailed explanation

There's actually two ways to go about solving this problem:

  1) Execute native scripting code
  2) Execute ActionScript code using an ExtendScript function

 

First we will examine the first approach:

The doScript() method can actually accept a few different types of parameters:

  1) It can accept a File object
  2) It can accept a string (of code to execute)
  3) It can accept a reference to a function

All three of these parameter types can be used from ActionScript, but there are points to keep in mind:

Method #1

If method #1 is used, you can save a script file to a specific location and execute the file. However, you must pass in an ExtendScript File object which is very different than an ActionScript one. This is complicated by the fact that ActionScript does not recognize ExtendScript File objects. One way to work around this problem would have been to use eval() to construct a File object inside the doScript() call, but you can't do that from ActionScript directly. Instead, you  would need to execute the doScript in ExtendScript:

var fileString:String = "File(\"" + filePath + "\")";
var evalFileString:String = "eval('" + fileString + "')";
CSXSInterface.getInstance().evalScript("app.doScript",evalFileString,"","[]",String(UndoModes.ENTIRE_SCRIPT.value),scriptName);

Method #2

A much simpler way to handle this would be to pass in the string rather than trying to construct an ExtnedScript File object. Assuming the ExtendScript string is coming from a file you could do that like this:

var script:String = "";
var scriptFile:File = new File();
scriptFile.url = url;// this is the file path
if (scriptFile.exists) {
  var stream:FileStream = new FileStream();
  stream.open(scriptFile, FileMode.READ);
  script = new String(stream.readUTFBytes(stream.bytesAvailable));
  stream.close();
}
if(script != ""){
  app.doScript(script,ScriptLanguage.JAVASCRIPT,[],UndoModes.ENTIRE_SCRIPT,scriptName);
}

Method #3

Generally when using doScript in ExtendScript, I pass in a variable which references an ExtendScript function object. Something like this:

function foo(){
  alert("Foo!");
}
app.doScript(foo);

The problem is that from ActionScript it's not quite so straight-forward. Using this method is not very difficult, but it requires a different approach than the first two. For simplicity sake, I'm assuming that the ExtendScript code exists at the time of compiling the extension:

    1) First use the method described here to create a "jsxInterface" object which references your saved ExtendScript file.
    2) In you ExtendScript file create your function. We'll call it "doSomethingComplicated()".
    3) From ActionScript simply call your ExtendScript function like so:

app.doScript(jsxInterface.doSomethingComplicated,ScriptLanguage.JAVASCRIPT,[],UndoModes.ENTIRE_SCRIPT,scriptName);

 

Now let's examine the second approach:

Until now I am assuming that you are executing native script code (in my examples I used ExtendScript). You might want to write your code in ExtendScript and execute that . How would we go about doing that since doScript will not accept ActionScript as an argument?

My solution is to have ActionScript call ExtendScript which in turn calls ActionScript. How do you do that you might ask? Thank Bob Stucky for coming up with the following method:

First, we write ExtendScript code (in our myScript.jsx file) to create an object which can act as a reference to our extension:
// Create a global nameSpace object to hold all our globals
gUniqueNS = {};
// asWrapper is set via initWrapper. It is an object reference to the AS Side of our extension
gUniqueNS.asWrapper = {};
gUniqueNS.initWrapper = function( wrapper ) {
   gUniqueNS.asWrapper = wrapper;
}
Next we create an ExtendScript interface as described in Zak's post which I reference above in method #3:
[ Embed (source= "myScript.jsx" , mimeType= "application/octet-stream" )]
private static var myScriptClass:Class;
var  jsxInterface:HostObject = HostObject.getRoot(HostObject.extensions[0]);
jsxInterface.eval( new myScriptClass().toString());
Next, we need to initialize our ExtendScript code with a reference to our ActionScript side of the extension, so in ActionScript we write:
jsxInterface.gUniqueNS.initWrapper(this);
Our ActionScript function is like any other:
public function doSomethingComplex(){
  //Our really cool code
}
Our ExtendScript simply calls this function:
gUniqueNS.executeActionScript = function(){ gUniqueNS.asWrapper.doSomthingComplex(); }
Finally, our ActionScript calls the ExtendScript (which in turn calls the ActionScript:
app.doScript(jsxInterface.gUniqueNS.executeActionScript,ScriptLanguage.JAVASCRIPT,[],UndoModes.ENTIRE_SCRIPT,scriptName);

+
This work is licensed under a Creative Commons Attribution-Noncommercial-Share Alike 3.0 Unported License. Permissions beyond the scope of this license, pertaining to the examples of code included within this work are available at Adobe.

Report abuse

Related recipes