Add Undo to Your Script

Your first script runs just fine; but what if you run it by accident? Find out how to add a user friendly One-Step-Undo-All!

Javascripts are great. They can do lots of things, and they can do so at a fairly fast speed, and they can do the same thing again and again without making any mistake… well, that’s the intention, at least. Sometimes you start up a script and you see it go wild, moving stuff around and deleting and copying — and you realize, -Wait! That’s not what I wanted!  Perhaps you fired up the wrong script, or at the wrong time, or perhaps the script does not do exactly what you were hoping.

Time to choose Undo… but unfortunately, most scripts don’t let you undo in one step. That is, if the script performed 100 actions, you’ll have to Undo 100 times! That’s bad. Even worse, it doesn-t help that InDesign’s Undo seems to have a mind of its own, and when you hit Undo, the view jumps to some random page, rather than the one you were hoping to see.

Fortunately, a tiny bit more scripting can help considerably! (Unfortunately, this only works when scripting CS4 and newer.)

The Original Script

Let’s start with a nice small Javascript that I wrote just the other day to expand US state abbreviations to their full form. For each of the items in the text array at the top, it searches for the abbreviation and replaces it with the full name:

var abbrevs = [ [ "Alabama", "AL" ], [ "Alaska", "AK" ],
[ "Arizona", "AZ" ], [ "Arkansas", "AR" ]
]; // Some more states left out for brevity
app.findGrepPreferences = app.changeGrepPreferences = null;
for (a in abbrevs)
{
app.findGrepPreferences.findWhat = "b"+abbrevs[a][1]+"b";
app.changeGrepPreferences.changeTo = abbrevs[a][0];
app.activeDocument.changeGrep();
}
app.findGrepPreferences = app.changeGrepPreferences = null;

For this trick, it’s not important that you understand everything that is going on in that script, but here’s a quick rundown anyway: Each element in the array at the top (the stuff in square brackets) contains two elements in turn, the full name and the USPS abbreviation. The script uses a for command to loop through each of elements in the “main” array (it does it (a in abbrevs) times), and replaces the second element of each (the abbreviation) with the first one (the full name). I use GREP for convenience: it’s case sensitive by default and finds just entire words with some added codes. After all, you definitely don’t want to have it replace each occurrence of “ar” in your text.

That’s all fine and dandy, and if you run the script you’ll see it works as expected. But if you change your mind, and try to Undo- The script performs up to 59 separate Find-and-Change operations (well, this one does only four, but the original does them all), and so you would need to Undo 59 times. You might think, “oh I can just write a script that does an undo 59 times,” but if an abbreviation wasn’t found, it won’t register as an Undo-able action, so you can’t count on it being 59 all the time. Sigh.

So here’s how to collapse the entire script into one single Undo, in two easy steps:

  1. Wrap the script in a single “function”.
  2. Add a code fragment at the top.

Sounds easy? It is! But please always make sure to keep a copy of the original in case something goes wrong while you edit it. (That’s just a good version-control practice for any scripting work.)

Step 1

A function in Javascript begins with the word “function”, followed by the name of the function, and optionally followed by a list of arguments. In this case, we don’t need to bother with arguments, so we’ll leave it blank. The function “body”, as it is known, should be inside matching curly braces at the very top and bottom:

function doReplace ()
{
var abbrevs = [ [ "Alabama", "AL" ], [ "Alaska", "AK" ],
[ "Arizona", "AZ" ], [ "Arkansas", "AR" ]
]; // Some more states left out for brevity
app.findGrepPreferences = app.changeGrepPreferences = null;
for (a in abbrevs)
{
app.findGrepPreferences.findWhat = "b"+abbrevs[a][1]+"b";
app.changeGrepPreferences.changeTo = abbrevs[a][0];
app.activeDocument.changeGrep();
}
app.findGrepPreferences = app.changeGrepPreferences = null;
}

Theoretically, you can add this function wrapper to every script, even ones that contain functions of themselves. Take care to use a unique name for the function. Javascript doesn’t have many “reserved” words (function is one; so are var, break, for, while, if, else, return, and switch; there are some more), and you cannot use one of these. You also cannot use digits at the start, and – most important! – the name should not appear anywhere else in the script, so abbrevs is out as well. On top of that, ExtendScript reserves lots of names for InDesign objects: Page, Font, Hyperlink, etc. Best is to use something personal with a combination of lowercase and uppercase, like myCoolFunction.

Step 2

Add these lines at the very top of the script:

if (parseFloat(app.version) < 6)
doReplace();
else
app.doScript(doReplace, ScriptLanguage.JAVASCRIPT, undefined, UndoModes.ENTIRE_SCRIPT, "Expand State Abbreviations");

Of course you should replace the highlighted names of the function with the name you used -note that it appears twice.

This is InDesign version-friendly code, because the very first line checks if it’s running under an earlier version than CS4 (which unfortunately has the version number “6”). CS3 and earlier versions do not support “Undo” in its “doScript” command. Of course you know what version your script will run on, but you may want to hand it over to someone who has an older version, so it’s nice to have this little check.

You also need to change the Undo name at the end of the bottom line (that’s the third highlighted text in the code above). This is the text that appears in the Edit menu; in this case, it will display “Undo Expand State Abbreviations”, and after have you undone the deed it will show as “Redo Expand State Abbreviations”.

Save the modified script in the usual place to make it appear in your Scripts panel, and try it out on a simple document. If all’s gone well, the script will run and you’ll see its usual behavior, but if you check the Edit menu, you will find you only have one step to undo: the entire script!

Bookmark
Please login to bookmark Close

This article was last modified on December 21, 2021

Comments (15)

Leave a Reply

Your email address will not be published. Required fields are marked *

Loading comments...