Welcome, Guest. Please Login
Rorohiko Workflow Resources
  Welcome the Message Board of Rorohiko Workflow Resources. To register, please e-mail [email protected] - automatic registration has been turned off because of relentless spambots and spammers. This Message Board is not actively monitored. If you have an urgent question, please e-mail [email protected]. If you post it here, it will probably be many weeks before we notice.
  HomeHelpSearchLogin  
 
Page Index Toggle Pages: 1
Send Topic Print
APID and efficient subroutine libraries in CS3/CS4 (Read 29036 times)
Kris Coppieters
YaBB Administrator
*****
Offline



Posts: 181
New Zealand
Gender: male
APID and efficient subroutine libraries in CS3/CS4
03/09/09 at 16:12:18
 
Below a method to implement an efficient subroutine library with APIDToolAssistant and InDesign CS3/CS4. If you have a large script, and use only a single .spln file, you cause InDesign to re-parse the whole script every time an event needs to be processed.

To avoid unnecessary reparsing, you can store a whole collection of subroutines in a separate .spln, load that .spln just once, and then call the subroutines and functions as needed.

There are many variations on the theme - what's shown here is just one approach.

First piece of the puzzle: if you have multiple .spln files loaded, they can call 'into' each other as long as they share the same component ID. So, when building a subroutine library, you need to make sure it uses the same component ID as your main .spln.

Here's a step-by-step example (assuming APID ToolAssistant is installed):

1) open APIDTemplate, and create a scripted plugin template file (say MyPlugin); save it to your desktop.

2) open APIDTemplate again, and now create a scripted plugin template file for the library (say MyLibrary); use the exact same password and copyright string as what you used for MyPlugin. Save it to your desktop.

Switch to MyLibrarySourceCSx.indd (where x = nothing, 2, 3, or 4 - it depends on what version of InDesign you are using). Make the Active Page Item Developer palette visible with the proxy page item selected.

Clear the list of subjects and enter loadLibrary in the event filter (you can concoct pretty much any other event name - loadMyNiceLib would work just as well, but I picked loadLibrary because it describes what the event will do).

Now select the page item below the proxy item, and inspect the start of the attached script code - it should look like

Code:
...
kScriptName = "MyLibrary";
kCompiledPassword = "...";
... 


Add a line to it so it reads:

Code:
...
kScriptName = "MyLibrary";
kComponentIDScriptName = "MyPlugin";
kCompiledPassword = "...";
... 


Scroll further down and change the line (which occurs twice - you need to change both of them):

Code:
...
var theComponentName = kScriptName + ";" + kCompiledPassword + ";" + kCopyright;
... 


into

Code:
...
var theComponentName = kComponentIDScriptName + ";" + kCompiledPassword + ";" + kCopyright;
... 


Click in the pasteboard area.

This change now forces the library to use MyPlugin;password and other stuff as its component ID instead of MyLibrary;password and other stuff - in other words: it now uses the same component ID as the main MyPlugin.

Open the MyLibrary.jsx file and enter the following code:

Code:
alert("loading library");

if (typeof gLibrary == "undefined")
{
  gLibrary =
  {
    function1: function(x) { return x + 1; },
    function2: function(x,y) { return x+y; }
  };
} 


This code will create a global variable gLibrary which contains an object with a set of functions. You can do anything similar - the main idea is to create some global object that carries your subroutines and functions.

The typeof setup (courtesy of Harbs) is there to test for the existence of gLibrary without ever mentioning var gLibrary;. By avoiding var gLibrary;, through a JavaScript quirk, we guarantee that gLibrary is truly global - even if the code above is wrapped by APID in some unseen curly brackets.

Otherwise, if the code above is actually wrapped in some invisible outer curly braces (as some APID versions do), the mere mention var gLibrary; would make gLibrary local to this invisible outer code block - and it would disappear from memory at the end of the block.

The alert allows us to notice when this script code is executed - the idea is that it will only be executed once.

Click the 'Select this frame to compile a release version of MyLibrary.spln (with componentId)' page item to compile the library into a .spln. This .spln is pretty 'inert' - it won't do anything by itself.
Back to top
« Last Edit: 03/11/09 at 21:51:34 by Kris Coppieters »  

Kris
WWW  
IP Logged
 
Kris Coppieters
YaBB Administrator
*****
Offline



Posts: 181
New Zealand
Gender: male
Re: APID and efficient subroutine libraries
Reply #1 - 03/09/09 at 16:39:31
 
Now the next step. Switch to MyPluginSourceCSx.indd, and select the proxy page item. Clear the list of subjects and, for the sake of argument, set the event filter to docLoaded, docSave

Open MyPlugin.jsx in a text editor, and add the following code:

Code:
if (typeof gLibrary == "undefined")
{
  var theDoc = GetParentDocument(theItem);
  if (theDoc != null)
  {
    var library = theDoc.loadedScriptedPlugins("mylibrary")[0]; // all lowercase, without .spln
    if (library != undefined)
    {
	library.handleScriptEvent("loadLibrary");
    }
  }
}

if (typeof gLibrary != "undefined")
{
  alert(gLibrary.function1(12));
}

function GetParentDocument(theItem)
{
  var err;
  try
  {
    var prevItem = null;
    while (prevItem != theItem && theItem != null && ! (theItem instanceof Document))
    {
	prevItem = theItem;
	theItem = theItem.parent;
    }
    if (! (theItem instanceof Document))
    {
	theItem = null;
    }
  }
  catch (err)
  {
    theItem = null;
  }

  return theItem;
} 



This code first checks whether the global gLibrary object has been defined. If not, then the library .spln file is found by name, and sent the loadLibrary event.

If the gLibrary has already been defined, then the library .spln file is not accessed and no time is wasted re-scanning the code behind gLibrary

Note that MyLibrary.spln file is found by stripping off the .spln file name extension, converting all letters to lower case ("MyLibrary.spln" -> "mylibrary"), and calling app.activeDocument.loadedScriptedPlugins("mylibrary"). This returns a 1-element array of ScriptedPlugin objects - hence the additional [0] at the end.

Once we have the ScriptedPlugin object, we can send it a handleScriptEvent with the loadLibrary event code. Because MyLibrary.spln and MyPlugin.spln share the same component ID, this is allowed.

An alternate, slightly easier approach would be to not choose loadLibrary or so as the library event code, but instead use some event code like externalLoadMyWonderfulLibrary. If the event code is prefixed with external... then APID will not check for matching component IDs.

So, in MyLibrarySourceCSx.indd you'd use externalLoadMyWonderfulLibrary in the event filter, and in MyPlugin.jsx, you'd call library.handleScriptEvent("externalLoadMyWonderfulLibrary"); without need for matching component IDs.

Compile a release version of MyPlugin.spln, and quit InDesign.

We should now have MyLibrary.spln, MyPlugin.spln and APID ToolAssistant installed. Launch InDesign, and open or create a document.

You should first see a dialog "loading" - this is the library code being loaded. Then a next dialog shows '13' - this is alert(gLibrary.function1(12));

Now save the document you just opened - MyPlugin also executes on docSave. You should just get a dialog with '13' - the "loading" dialog should not reappear.

Open or create another document - again, the '13' dialog appears - and "loading" should not appear - gLibrary has been loaded once only and is shared between multiple documents.

The code works as-is on CS and CS2 too, but the library is reloaded every time because the data is not persistent between sessions Sad
Back to top
« Last Edit: 03/09/09 at 22:05:04 by Kris Coppieters »  

Kris
WWW  
IP Logged
 
Page Index Toggle Pages: 1
Send Topic Print