Copy link to clipboard
Copied
I'm searching a book using ExtendScript. I can do it opening each document individually using the code below. Is there a better way; possibly something like: var book = app.ActiveBook; var comp = book.FirstComponentInBook; comp = comp.NextBookComponentInDFSOrder? I ran across some script using these objects and methods from 2013, but it didn't work with my test book.
//Code that works to step through all docs in book
doc = app.FirstOpenDoc;
while (doc.ObjectValid()) {
doc = SimpleOpen(doc.Name);
doc = app.ActiveDoc;
// findAndChange(doc)
doc = doc.NextOpenDocInSession; // Open next document in book
}
Here is a shell that I use all the time. The idea is that if the document is already open, it is used; otherwise, the script opens it. When you are done processing it, the script will only save and close documents that it opened. This is consistent with FrameMaker's built-in behavior. You never want to save and close an open document unless you are sure you get the desired result.
function processBook (book) {
var bookComp = 0, doc = 0;
// Loop through all of the components in
...
I use a program called FileLocator Pro--the free version is called Agent Ransack--to search my file library of scripts. I can usually find what I am looking for in my collection to use in a new script. Also, you may want to consider creating a scipt file that has your common functions. You can put it in the startup folder and those functions will be available from any other script. It keeps you from having to repeat a bunch of code in multiple scripts.
You can't have an active book and an active document at the same time. That is why you need the ObjectValid method to test what you have active. In my scripts that work on either an active book or document, I usually use something like this:
main ();
function main () {
var book, doc;
book = app.ActiveBook;
if (book.ObjectValid () === 1) {
// Call a function to process the book.
}
else {
doc = app.ActiveDoc;
if (doc.ObjectValid () === 1) {
...
Copy link to clipboard
Copied
Here is a shell that I use all the time. The idea is that if the document is already open, it is used; otherwise, the script opens it. When you are done processing it, the script will only save and close documents that it opened. This is consistent with FrameMaker's built-in behavior. You never want to save and close an open document unless you are sure you get the desired result.
function processBook (book) {
var bookComp = 0, doc = 0;
// Loop through all of the components in the book.
bookComp = book.FirstComponentInBook;
while (bookComp.ObjectValid ()) {
if (bookComp.ComponentType === 512) {
// Get the document returned in a JavaScript object.
doc = getDocument (bookComp.Name, undefined, true);
if (doc) {
book.StatusLine = "Processing " + File(bookComp.Name).displayName;
// Call the function to process the document.
processDoc (doc);
// If the document was opened by the script, save it and close it.
if (doc.openedByScript === true) {
doc.SimpleSave (bookComp.Name,false);
doc.Close (true);
}
}
}
bookComp = bookComp.NextBookComponentInDFSOrder;
}
// Reset the book status line.
book.StatusLine = "";
}
function getDocument (filename, structApp, visible) {
// See if the document is already open.
var doc = docIsOpen (filename);
if (doc) {
return doc;
} else {
// The document is not already open, so open it.
return openDocOrBook (filename, structApp, visible);
}
}
function docIsOpen (filename) {
// Make a File object from the file name.
var file = File (filename);
// Uppercase the filename for easy comparison.
var name = file.fullName.toUpperCase ();
// Loop through the open documents in the session.
var doc = app.FirstOpenDoc;
while (doc.ObjectValid () === 1) {
// Compare the document’s name with the one we are looking for.
if (File (doc.Name).fullName.toUpperCase () === name) {
// The document we are looking for is open.
doc.openedByScript = false;
return doc;
}
doc = doc.NextOpenDocInSession;
}
}
function openDocOrBook (filename, structApp, visible) {
var i = 0, docOrBook = 0;
// Get default property list for opening documents.
var openProps = GetOpenDefaultParams ();
// Get a property list to return any error messages.
var retParm = new PropVals ();
// Set specific open property values to open the document.
i = GetPropIndex (openProps, Constants.FS_AlertUserAboutFailure);
openProps[i].propVal.ival = false;
i = GetPropIndex (openProps, Constants.FS_MakeVisible);
openProps[i].propVal.ival = visible;
i = GetPropIndex (openProps, Constants.FS_FileIsOldVersion);
openProps[i].propVal.ival = Constants.FV_DoOK;
i = GetPropIndex (openProps, Constants.FS_FileIsInUse);
openProps[i].propVal.ival = Constants.FV_ResetLockAndContinue;
i = GetPropIndex (openProps, Constants.FS_FontChangedMetric);
openProps[i].propVal.ival = Constants.FV_DoOK;
i = GetPropIndex (openProps, Constants.FS_FontNotFoundInCatalog);
openProps[i].propVal.ival = Constants.FV_DoOK;
i = GetPropIndex (openProps, Constants.FS_FontNotFoundInDoc);
openProps[i].propVal.ival = Constants.FV_DoOK;
i = GetPropIndex (openProps, Constants.FS_RefFileNotFound);
openProps[i].propVal.ival = Constants.FV_AllowAllRefFilesUnFindable;
if (structApp !== undefined) {
i = GetPropIndex (openProps, Constants.FS_StructuredOpenApplication);
openProps[i].propVal.sval = structApp;
}
// Attempt to open the document.
docOrBook = Open (filename, openProps, retParm);
if (docOrBook.ObjectValid () === 1) {
// Add a property to the document or book, indicating that the script opened it.
docOrBook.openedByScript = true;
}
else {
// If the document can't be open, print the errors to the Console.
PrintOpenStatus (retParm);
}
return docOrBook; // Return the document or book object.
}
Copy link to clipboard
Copied
Thanks Rick...I saw functions from this code in your FindChangeBatch script, but haven't had time to trace each of the function "gets" to have a clear idea of how it worked. This is exactly what I was looking for. I agee I don't want the script saving and closing books. The editor needs to do that after confirming the documents (chapters) reflect the desired changes.
BTW, thanks for urging me (and others) to modularize our code into functions. It took me a couple of days to get the hang of it, but I love what it did to the code of my longest and most complex script...and like you said, now I can quickly build new scripts using functions I've already written and tested. Now I just have to figure out a way to organize and store them.
Copy link to clipboard
Copied
I use a program called FileLocator Pro--the free version is called Agent Ransack--to search my file library of scripts. I can usually find what I am looking for in my collection to use in a new script. Also, you may want to consider creating a scipt file that has your common functions. You can put it in the startup folder and those functions will be available from any other script. It keeps you from having to repeat a bunch of code in multiple scripts.
Copy link to clipboard
Copied
Took me a while to get back to the second part of your post regarding ways to file functions so I can find them later. I'm going to start by creating a script file for my common functions, and may purchase FileLocator Pro if it becomes too unwieldy to manage. Also, thanks for the tip on putting the folder with my favorite scripts in the FM user startup folder. I was completely unaware you could do that...and Adobe's doesn't say anything about it under "Managing Scripts" in the user guide...very useful information.
Copy link to clipboard
Copied
I finally got around to your script for processing a book. I must be doing something wrong. I have a book open in FrameMaker. Everything tells me it's a valid book object, but it does not appear as a valid object. Using the code below...
var doc = app.ActiveDoc;
$.writeln (doc.Name);
var odocName = doc.Name;
var book = app.ActiveBook;
$.writeln(book);
var bookComp = book.FirstComponentInBook;
$.writeln(bookComp);
var compType = bookComp.ComponentType;
$.writeln("Component Type = " + compType);
I get the following results...
C:\Users\fight\Desktop\Boeing-737_CH01.fm
[object Book]
[object BookComponent]
Component Type = 0
While the ActiveDoc tests valid, neither book nor bookComp test valid. Without a valid book object, the processBook function won't work. Everything else in Frame treats the book as a book.
Copy link to clipboard
Copied
You can't have an active book and an active document at the same time. That is why you need the ObjectValid method to test what you have active. In my scripts that work on either an active book or document, I usually use something like this:
main ();
function main () {
var book, doc;
book = app.ActiveBook;
if (book.ObjectValid () === 1) {
// Call a function to process the book.
}
else {
doc = app.ActiveDoc;
if (doc.ObjectValid () === 1) {
// Call a function to process the document.
}
}
}
Copy link to clipboard
Copied
Thanks Rick...kind of an "aha" moment for me. Once you understand, the solution is relatively simple. Basically, test every object you are trying to select before trying to access it. The workaround I came up closes all the open documents in the book and reopens them individually for processing. Basically, using brute force. This should be a lot easier.
Copy link to clipboard
Copied
I use brute force sometimes just to get things to work. Then the "aha" moment comes and I can sheepishly clean things up. Fortunately, most clients don't actually look at my code 🙂
Copy link to clipboard
Copied
Thanks Rick...good to know the experts do the same thing. I'm coming at this with zero computer coding training. However, spent eight years developing a set of editing tools in Word VBA, with well worn copies of Mansfield's Mastering VBA dating back to 2010. Now trying to translate the FindChange modules into ExtendScript so editors don't have to jump between two docs to make the changes. My scripts aren't pretty, but I'll take your advice and improve them as I get smarter. Thanks.
Copy link to clipboard
Copied
I am glad to see that you are diving in and experimenting. That is the best way to learn. I enjoy helping when I can.