How to make a script run automatically when you save a Framemaker document

Community Beginner ,
Jan 24, 2019

Copy link to clipboard

Copied

I wrote a script that actually works, but I'd like for it to automatically run whenever I save the Framemaker (19) document. It seems like there should be a command for that but I can't find it. Thanks!

TOPICS
Scripting

Views

966

Likes

Translate

Translate

Report

Report
Reply
Community Guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more

1 Correct Answer

Mentor , Jan 25, 2019
Russ Ward Mentor , Jan 25, 2019
Hi juliem31820066,This is done through the Notify() callback. Basically, you put the desired code into some function, then add a notification for it to fire when a document is saved. Effectively, the script as a whole is always running with Notify() listening, which then triggers the desired functionality when a document is saved.I have a sample here:FrameMaker ExtendScript Samples - West Street Consulting It is 06.01 - NOTIFICATION_-_Notify_on_doc_open_and_close. This script shows an example of...

Likes

Translate

Translate
Jump to answer Jump to answer
Mentor ,
Jan 25, 2019

Copy link to clipboard

Copied

Hi juliem31820066,

This is done through the Notify() callback. Basically, you put the desired code into some function, then add a notification for it to fire when a document is saved. Effectively, the script as a whole is always running with Notify() listening, which then triggers the desired functionality when a document is saved.

I have a sample here:

FrameMaker ExtendScript Samples - West Street Consulting

It is 06.01 - NOTIFICATION_-_Notify_on_doc_open_and_close. This script shows an example of code execution when a document is opened or closed. You could very easily convert it to a save action by changing "FA_Note_PreQuitDoc" and "FA_Note_PostOpenDoc" in the script to "FA_Note_PreSaveDoc" and "FA_Note_PostSaveDoc".

Congratulations on getting a script to work, especially if this is your first time. I remember the first FDK client I got to work. All it did was toggle element brackets with a keyboard shortcut, but it seemed as if I were Hercules and had opened a massive portal to a forbidden paradise. In retrospect, it was a big deal. The ability to customize my tools has made all the difference in my career.

Russ

Likes

Translate

Translate

Report

Report
Reply
Community Guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
Community Beginner ,
Jan 25, 2019

Copy link to clipboard

Copied

Hi Russ,

Thank you! I was able to use bits and pieces of other scripts I found, and then patch them together and modify them to make them do what I want. So I don't know if I technically "wrote" it, but I did it without asking for help ... until now.

When I added the notification script, I noticed that the script now shows up in my Registered Scripts in the script library. It does run when I save, but nothing else works correctly. I've tried a lot of things but nothing makes it work correctly. I think I only need the PostSaveDoc part, because I want the rest of the script to run after the save. Can you tell what I'm doing wrong?

----------

#target framemaker

/* This script will see if the Framemaker filename is the template file. If it is, it will see if the filename matches the text on the reference page. If it does not match, it will run the rest of the script to update the reference page text to match the filename.  */

 

    var PT = 65536;      // Make a constant for the points metric value.

    var doc = app.ActiveDoc;

    var name = doc.Name;

    var exitProg = false;

    var page =  doc.FirstRefPageInDoc;

// Notify () code to make the script auto run.

Notification(Constants.FA_Note_PostSaveDoc , true);

Notification(Constants.FA_Note_PreSaveDoc , true);

function Notify (note, object, sparam, iparam)

{

    switch (note)

    {

          case Constants.FA_Note_PostSaveDoc:

      

              correctFileA ();

            break;  

          

           case Constants.FA_Note_PreSaveDoc:

      

              correctFileA ();

            break; 

     }

}

// The script below works when the notification code is removed. (need to call the first function)

function correctFileA () {

    if (name.indexOf("iodp2template_") === -1){    //If the file name does NOT contain the string, the program ends. Otherwise, it continues.

        exitProg = true;

            alert ("This is not a template file");

       } else {

            main ();

    }

}

function main () { // This will only run the script if the file name and text in the frame are not the same.

    var textFrame = getTextFrame (page, "File_name");

    if (getSystemVariableValue ("Filename (Short)", doc) === getText (textFrame, doc)) {

            exitProg = true;

            alert ("Script stopped. File name is not changed");

     }  else {

        process ();

        alert ("file name changed");

    }

}

function process () {

  

    delFileNameFrame ();

    addFileName ();

    }

function delFileNameFrame () { // looks for an existing frame with a specific flow name and deletes it

    var page, textFrame;

    page =  doc.FirstRefPageInDoc;

    textFrame = getTextFrame (page, "File_name");

    // If the text frame is found, delete it.

    if (textFrame) {

        textFrame.Delete ();

    }

}   

function getTextFrame (page, flow) {   // This function looks for  a text frame.

         

        var graphic;

        

        graphic = page.PageFrame.FirstGraphicInFrame;

        while (graphic.ObjectValid () === 1) {

            if (graphic.constructor.name === "TextFrame") {

                if (graphic.Flow.Name === flow) {

                    return graphic;

                }

            }

            graphic = graphic.NextGraphicInFrame;

        }

}

function addFileName () {  //adds text box to Reference page and fills it with the desired text

    var newRefPage = doc.FirstRefPageInDoc;

    var addTextFrame = doc.NewTextFrame (newRefPage.PageFrame);

    var pgf = addTextFrame.FirstPgf;

    addTextFrame.Flow.Name = "File_name";

    var textLoc = new TextLoc (pgf, 0);

    addTextFrame.Width  = 288 * PT;

    doc.AddText (textLoc, getSystemVariableValue ("Filename (Short)", doc));

    var filename = "";

    return filename;

}

function getSystemVariableValue (varName, doc) {  //gets the variable

   

    var masterPage, textFrame, pgf, textLoc, varObj, value = "";

    // Get the first master page in the document.

    masterPage = doc.FirstMasterPageInDoc;

    // Add a temporary text frame to the master page.

    textFrame = doc.NewTextFrame (masterPage.PageFrame);

    textFrame.Width  = 288 * PT;

    // Add the variable to the first paragraph in the temporary text frame.

    pgf = textFrame.FirstPgf;

    textLoc = new TextLoc (pgf, 0);

    varObj = doc.NewAnchoredFormattedVar (varName, textLoc);

    if (varObj.ObjectValid () === 1) {

        // Get the value of the variable.

        value = getText (varObj.TextRange, doc);

    }

    // Delete the temporary text frame.

    textFrame.Delete ();

   

    return value;

}

function getText (textObj, doc) {      // Gets the text from the text object or text range.

   

    var text = "", textItems, i;

   

    // Get a list of the strings in the text object or text range.

    if (textObj.constructor.name !== "TextRange") {

        textItems = textObj.GetText(Constants.FTI_String);

    } else {

        textItems = doc.GetTextForRange(textObj, Constants.FTI_String);

    }

    // Concatenate the strings.

    for (i = 0; i < textItems.len; i += 1) {

        text += (textItems.sdata);

    }

    return text; // Return the text

}

Likes

Translate

Translate

Report

Report
Reply
Community Guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
Advocate ,
Jan 25, 2019

Copy link to clipboard

Copied

Try to use the information that is passed to the event handler to make sure your code runs on the right document. You have to assume that the event handler has no access to variables unless they are global and even then I would be cautious about it. The object passed to the event handler is usually the document. You can test this by putting an alert in the event handler that shows the note number and the object.name - that should be your document name. If that name is correct, the object is the document to pass to the function that works on that document.

I am not saying that this will solve the problem but it makes the code safer against unexpected things that may be happening. When using event handlers, there is not really a way to single-step through the code from the ExtendScript toolkit, as control is passed to FrameMaker and FM calls your Notify function, i.e. it is not the ESTK that runs your code.

Have you tried running the correctFileA( ) function on the active document without going through the event handler? First single-step that, then make sure all the parameters are correct in the event handler by putting lots of alerts into the code, and remove the alerts when the result is right. It can be a hassle, but you really have to go small step by small step.

Good luck

Likes

Translate

Translate

Report

Report
Reply
Community Guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
Mentor ,
Jan 25, 2019

Copy link to clipboard

Copied

Hi,

The script is a bit too long for me to troubleshoot in detail, but I may not need to, as I think your problem is caused by some basic mishandling of variables. You define several global variables at the beginning, like 'doc' and 'name', but then I don't see anywhere that you update them before attempting to use them. Like how 'name' is referenced in the first line of correctFileA(), but I don't see anywhere that it has been updated for current conditions after the notification fires (that is, to reflect the currently-active doc).

Again, with the disclaimer of not having studied the code in depth, I think your problem is with how you are using these variables as globals. I think that when the notification fires, you should use the active doc at that time, which is sent to Notify() as 'object'. Use that variable to determine 'page', 'name', etc. on the spot, then send those values directly to correctFileA() as function arguments.

In general, it is always best to avoid globals wherever possible. When you need to know something, read it on the spot based on current conditions. This is especially true when working with documents, because the active document can change.

So, I think your problem is somewhere in there; that is, setting globals when the script initiates but never updating them. Remember that FM does not execute the whole script when you save the doc... just the code inside Notify(). So your globals remain whatever they were when the script first ran.

Debugging notification code is always harder, because it is not so straightforward to set up breakpoints and such like it is when running a script from the beginning. Sometimes you have to be creative.

Russ

Likes

Translate

Translate

Report

Report
Reply
Community Guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
Community Beginner ,
Jan 25, 2019

Copy link to clipboard

Copied

Thank you. I guess I just don't get it. My script ( correctFileA() ) worked perfectly until I tried to add the notification script. I went ahead and moved all the variables within the functions, checked everything and it's all working (without the notification script). So instead of copying it all back over, I made a simple script to try the notification with. The script works alone, so I know the problem is with how I wrote the notification, but I just don't know enough about scripting to make it work.

#target framemaker 

var doc = app.ActiveDoc;

//Notify () code to make the script auto run.

Notification(Constants.FA_Note_PostSaveDoc , true);

function Notify (note, object, sparam, iparam) {

    var doc = app.ActiveDoc;

    switch (note) {

        case Constants.FA_Note_PostSaveDoc:

        correctFileA ();

        break;   

        }

}

function correctFileA () {

    var doc = app.ActiveDoc;

    var pgf = doc.FirstPgfInDoc;

    if (pgf.ObjectValid () === 1) {

        alert("This is here");

        }

}

Likes

Translate

Translate

Report

Report
Reply
Community Guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
Enthusiast ,
Jan 25, 2019

Copy link to clipboard

Copied

Hi juliem31820066,

your script above works fine.

BUT:

1. You have to give it a name (save it).

2. Don't use ctrl-S to save, but click on the save icon.

Likes

Translate

Translate

Report

Report
Reply
Community Guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
Advocate ,
Jan 28, 2019

Copy link to clipboard

Copied

Klaus, that is a great response. It is pretty obvious that FrameMaker has no way of knowing where to go for the script when that script only lives in memory on the ESTK. I never even imagined you would create a long script like this and never even save it in case your computer shuts down or something else happens. It is always a good idea to save a script, even if it is to a temp location.

Likes

Translate

Translate

Report

Report
Reply
Community Guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
Enthusiast ,
Jan 28, 2019

Copy link to clipboard

Copied

Hi Jang

I never even imagined you would create a long script like this and never even save it

I've tested the first and second script just by copying it from here to ESTK just for testing. So I didn't save it.

So they MUST fail.

So this can happen 😉

Likes

Translate

Translate

Report

Report
Reply
Community Guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
Enthusiast ,
Jan 28, 2019

Copy link to clipboard

Copied

To make it clear:

var oDoc = app.ActiveDoc

    

Notification(Constants.FA_Note_PostMouseCommand,true);

function Notify(note, object)

{

    switch (note)

            {

            case Constants.FA_Note_PostMouseCommand:

                {

                ShowAlert();

                break;

                }

            }

}

function ShowAlert()

{

    alert("A");

}

1. Copy the sourcecode to ESTK

2. Save it.

3. Run this script. On mouseclick you'll get the alert "A".

4. Change code line 19 to alert("B");

5. DON'T SAVE

6. remove the old script from script library - registert

7. run the (unsaved) script again from ESTK.

8. You'll see alert "A".

So this shows, how important it is to save before debugging.

Likes

Translate

Translate

Report

Report
Reply
Community Guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
Advocate ,
Jan 28, 2019

Copy link to clipboard

Copied

Yes, the event handler is called from the script that was registered - and this is the saved script. As Klaus has excellently shown. As long as the event handler code is not saved, you can execute it and it gives no errors, but the call back from FrameMaker goes into nirvana and does nothing at all.

For scripts that do not have an event handler (technically a callback function) there is no issue with running and even debugging them without saving first. Such scripts (and their execution) are controlled by the ESTK. The callbacks are not. That is also why you cannot single-step an event handler in the ESTK.

Likes

Translate

Translate

Report

Report
Reply
Community Guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
juliem31820066 AUTHOR LATEST
Community Beginner ,
Jan 28, 2019

Copy link to clipboard

Copied

Thank you Klaus and 4everJang,

It looks like I was missing some brackets in the Notify () function, so now it's working ... sometimes. I'm running into some other issues now, but I'm not sure why. I'll work on it some more and hopefully figure it out. If not, I'll be back. Thanks!

Likes

Translate

Translate

Report

Report
Reply
Community Guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more