• Global community
    • Language:
      • Deutsch
      • English
      • Español
      • Français
      • Português
  • 日本語コミュニティ
    Dedicated community for Japanese speakers
  • 한국 커뮤니티
    Dedicated community for Korean speakers
Exit
0

ES: Passing a function variable value to another function

Participant ,
Aug 31, 2016 Aug 31, 2016

Copy link to clipboard

Copied

Hello fellows,

Let's say I have a function that includes a variable as follows:

opendoc = Open(<doc_name>, openParams, openReturnParams);

I'd like to use opendoc in another function to import file formats from a file into opendoc.

Is there a way to make opendoc global and accessible from another function?

Thank you for your responses in advance!

TOPICS
Scripting

Views

2.4K

Translate

Translate

Report

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

correct answers 1 Correct answer

Advocate , Sep 04, 2016 Sep 04, 2016

Hi Roman,

You are checking whether a paragraph format exists, not whether the first paragraph in the document has that format applied. If you are using the same template for all files, all files will have the same paragraph formats, hence the "if" to check whether the paragraph format exists will always yield 'true'. And when you check whether that paragraph format is the first format in the file, it will likely produce 'false', as it would be a strange coincidence if that paragraph format would

...

Votes

Translate

Translate
Advocate ,
Aug 31, 2016 Aug 31, 2016

Copy link to clipboard

Copied

Hi Rom,

I use global as well as local variables all the time. Also I have accustomed myself to declaring all variables, and using prefixes that help me see immediately what the scope and type of a variable is. Hence I would use the following:

var goOpenDoc;          /" g for global, o for object */

function ( whatever arguments you need, but not including goOpenDoc )

{

     var oTempDoc = Open sFileName, oaOpenParams, oaReturnParams );     /* s for string, oa for object array */

     if( oTempDoc.ObjectValid( ) )

     {

          goOpenDoc = oTempDoc;

     }

     ...

}

I hope that helps

Kind regards from Amsterdam

Jang

Votes

Translate

Translate

Report

Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Mentor ,
Aug 31, 2016 Aug 31, 2016

Copy link to clipboard

Copied

rombanks,

Jang's advice is accurate. For amateur coding, nothing wrong with it. However, a real programmer (which I am not one) would likely tell you to avoid globals and pass the variables directly. For example:

opendoc = Open(<doc_name>, openParams, openReturnParams);

ImportFormats(opendoc);

....

function ImportFormats(doc)

{

   /* code here to import formats into the doc */

}

But like I said, there is nothing functionally wrong with using a global as Jang suggests. The risk is that they get more difficult to maintain as the code gets longer and more complex.

I like the naming convention he suggests.

Russ

Votes

Translate

Translate

Report

Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Participant ,
Aug 31, 2016 Aug 31, 2016

Copy link to clipboard

Copied

Hi Jang,

I appreciate your prompt response!

IMHO, your approach definitely improves the code readability.

I read a lot about global variable declarations and also about an alternative approach called a 'Scoping' function (aka, the Main function that includes the rest).

For some reason, the opendoc variable value below is not passed to another function, so the format import doesn't work.

It only works if I merge the 2 functions  (SaveRegFM and ImportFmt) into one, making the variables local.

Here is a code snippet:

var doc = app.ActiveDoc;

var compAr = []; 

var book = app.ActiveBook; 

var bookComp = book.FirstComponentInBook; 

var opendoc;

Notification(Constants.FA_Note_PostOpenMIF, true);

function Notify(args, object, sparam, iparam)

{

   

  object.IsOnScreen = 0;

 

  switch (args)

         {

                 case Constants.FA_Note_PostOpenMIF:

                       

                        SaveRegFM(object);

                                                                                                                

                        break;

                       

         }

}

function SaveRegFM(doc)

{

        ...

         var saveParams = GetSaveDefaultParams();

         var returnParams = new PropVals();

        

         var i = GetPropIndex(saveParams, Constants.FS_FileType);

         saveParams.propVal.ival = Constants.FV_SaveFmtBinary13;

        

          if(!confirm("Would you like to save this file as FM in your working folder?"))

          return;

        

         doc.Save(<file_name_created_before>, saveParams, returnParams);

         doc.IsOnScreen = 0;

         doc.Close(true);

        

         var openParams = getOpenPrefs();

         var openReturnParams =  new PropVals();

       

         var tempdoc = Open(<file_name_created_before>, openParams, openReturnParams);

         opendoc = tempdoc;

}

ImportFmt(opendoc);

function ImportFmt() {

         if ((book.ObjectValid ()) && (bookComp.ExcludeBookComponent !==1)) {

                while (bookComp.ObjectValid ()) {

                   compAr.unshift (bookComp); 

                    bookComp = bookComp.NextBookComponentInDFSOrder;

                } 

               

               if (compAr[5].ObjectValid () == 1){               

                    var regFileName = compAr[5].Name;

                    var openRegFM = Open(regFileName, openParams, openReturnParams);

                    openRegFM.IsOnScreen = 0;

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

                            opendoc.SimpleImportFormats(openRegFM, Constants.FF_UFF_COND|Constants.FF_UFF_FONT|Constants.FF_UFF_PAGE |Constants.FF_UFF_PGF|Constants.FF_UFF_REFPAGE|Constants.FF_UFF_TABLE|Constants.FF_UFF_VAR|Constants.FF_UFF_XREF);

                            } 

                 }

                          openRegFM.Close(true);

            }        

  }

}

function getOpenPrefs() {

    var params, i;

   

    params = GetOpenDefaultParams();

   

    i = GetPropIndex(params, Constants.FS_RefFileNotFound);

    params.propVal.ival = Constants.FV_AllowAllRefFilesUnFindable;       

    i = GetPropIndex(params, Constants.FS_FileIsOldVersion);

    params.propVal.ival = Constants.FV_DoOK;

    i = GetPropIndex(params, Constants.FS_FontChangedMetric);

    params.propVal.ival = Constants.FV_DoOK;  

    i = GetPropIndex(params, Constants.FS_FontNotFoundInCatalog);

    params.propVal.ival = Constants.FV_DoOK;   

    i = GetPropIndex(params, Constants.FS_FontNotFoundInDoc);

    params.propVal.ival = Constants.FV_DoOK;   

    i = GetPropIndex(params, Constants.FS_LockCantBeReset);

    params.propVal.ival = Constants.FV_DoOK;       

    i = GetPropIndex(params, Constants.FS_FileIsInUse);

    params.propVal.ival = Constants.FV_OpenViewOnly;     

    return (params);

}

Best regards,

Roman

Votes

Translate

Translate

Report

Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Advocate ,
Aug 31, 2016 Aug 31, 2016

Copy link to clipboard

Copied

The problem might be with you using a combination of global variables and notification. Notification is a process where all kinds of things might be done by FM to ensure the code has its own unique context - which could be why your global variables do not work in that case. I would be very cautious in doing this and I would recommend following Russ' excellent advice - pass the oDoc object as a parameter to any function that requires access to it. In that way, your function always has a local pointer to the object it needs to work on. In that way, you avoid running into implementation details that might throw off your current code.

Votes

Translate

Translate

Report

Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Community Expert ,
Aug 31, 2016 Aug 31, 2016

Copy link to clipboard

Copied

You have a bunch of things going on here, but since a Notification script gets loaded on FrameMaker startup, there is only one line that should be outside of a function:

Notification (Constants.FA_Note_PostOpenMIF, true);

Everything else should be inside of a function so the code won't get called on FrameMaker startup. I would rough it out something like this:

Notification (Constants.FA_Note_PostOpenMIF, true);

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

    switch (args) {

        case Constants.FA_Note_PostOpenMIF:

            // You can put your confirmation dialog box here if you want.

            // ...

            processMif (object);

            break;

    }

}

function processMif (doc) {

   

    var templateDoc;

   

    templateDoc = getTemplateDoc (fileName);

    if (templateDoc) {

        importFormats (doc, templateDoc);

        templateDoc.Close (true);

    }

    saveAsFrameBinary (doc);

}

function getTemplate (fileName) {

   

    // Open the template here and return it.

    //....

   

    return doc;

}

function saveAsFrameBinary (doc) {

   

    // Save the MIF as Frame binary.

    //...

}

function importFormats (doc, templateDoc) {

   

    // Import formats here.

    //...

   

}

   

Global variables should be avoided at all costs, especially in scripts that load at FrameMaker startup.

Votes

Translate

Translate

Report

Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Participant ,
Sep 01, 2016 Sep 01, 2016

Copy link to clipboard

Copied

Guys,

Thank you all for your responses!

Rick, thank you for the prototype code!

Indeed, I saw a lot of discussions on the Javascript forums regarding the global variable woes. However, except for the 'maintenance nightmare', I didn't see any other logical explanation why global vars should be avoided. Are you aware of any?

Thanks again and have a good time of day! 🙂

Votes

Translate

Translate

Report

Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Advocate ,
Sep 01, 2016 Sep 01, 2016

Copy link to clipboard

Copied

Javascript variables are associative arrays. If some other code happens to use the same global variable name, the values will be overwritten without you knowing about it. Passing variables around is a safe method, as you are in control of what comes in and what goes out. This is the first principle of object oriented programming, which has become the basis for almost anything because of such characteristics. Unless there is a compelling reason to use globals in your code (which you should then always name such that nobody else will think of the same names to use in their code), you should simply not use them. The effort of passing objects into functions is not that big, anyway.

Votes

Translate

Translate

Report

Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Participant ,
Sep 01, 2016 Sep 01, 2016

Copy link to clipboard

Copied

Hi Jang,

Thank you for the explanations! But here we're again back to the original question. How can you pass a variable between functions if this variable is not global? How can you make a local variable visible to another function?

Thank you!

Best regards,

Roman

Votes

Translate

Translate

Report

Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Advocate ,
Sep 01, 2016 Sep 01, 2016

Copy link to clipboard

Copied

Hi Roman,

The document objects that are passed into the Notify( ) function are not copies of objects, but pointers to them. You pass that pointer to another function and that function works on the same object, via its local copy of the pointer. Names are local but as they point to the same object you are really working on one single document instance, even without introducing a global variable for it. The object pointer passed into a function as a parameter is reflected in the argument (which has a local name).

Example:

Notify( iNote, oObject, sParam, iParam )

{

     switch( iNote )

     {

          case Constants.FA_Note_PostOpenMIF :     SaveMIF2FM( oObject );

                                                                                break;

     }

}

SaveMIF2FM( oDoc )

{

     /* do something with the object */

     DoSomethingElse( oDoc );

}

DoSomethingElse( oThisDoc )

{

     /* work on oThisDoc */

}

In the above code, oObject, oDoc and oThisDoc are all pointing to the same object. I hope this helps to clarify how ExtendScript handles these.

Votes

Translate

Translate

Report

Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Participant ,
Sep 01, 2016 Sep 01, 2016

Copy link to clipboard

Copied

Hi Jang,

I see that you call the 2nd function within the 1st one and that's how you avoid defining a global variable.

In my case, we are talking about 2 different objects -- the original one is the MIF file that will later be saved as an FM file in a different directory. So the object specified in Notify() does not seem to be the object used in DoSomethingElse().

Thank you again for your great input!

Best wishes,

Roman

Votes

Translate

Translate

Report

Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Community Expert ,
Sep 01, 2016 Sep 01, 2016

Copy link to clipboard

Copied

Global variables are "evil" because they are exposed to other scripts can be overwritten or misused by other scripts. For example, if I have this:

var myGlobalVar = 123;

Notification (Constants.FA_Note_PostOpenMIF, true);

//... more code below ...

On FrameMaker startup, when this script gets loaded into memory, myGlobalVar becomes part of FrameMaker's current scripting session and is global in scope. That means that another script could overwrite it by defining it as something else.

var myGlobalVar = "abc";

Any part of your original script that accesses myGlobalVar would be broken or give you unexpected results. This may not concern you if you are only writing a few scripts for yourself, but it is better to be safe and stick with best practices.

By the way, the same thing can happen with functions. Functions defined at the top-level of a script are global in nature. So if you have this in one script:

function processDoc (doc) {

    //... function code.

}

and you define another processDoc function in another script, they will conflict when both are loaded into the FrameMaker's memory. The last script loaded will "win". One way to solve this problem is to use a single global object variable for the entire script. I will demonstrate this in another message.

Votes

Translate

Translate

Report

Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Community Expert ,
Sep 01, 2016 Sep 01, 2016

Copy link to clipboard

Copied

#target framemaker

// Create a global object to contain all of the script's code.

// Use a name that is unique that it won't collide with other variables.

var CP_PMIF = {}; // Carmen Publishing Inc. Process MIF.

// Add user-defined functions to the global object.

CP_PMIF.processMif = function (doc) {

   

    var templateDoc;

   

    templateDoc = CP_PMIF.getTemplateDoc (fileName);

    if (templateDoc) {

        CP_PMIF.importFormats (doc, templateDoc);

        templateDoc.Close (true);

    }

    CP_PMIF.saveAsFrameBinary (doc);

};

CP_PMIF.getTemplate = function (fileName) {

   

    // Open the template here and return it.

    // ...

   

    return doc;   

};

CP_PMIF.saveAsFrameBinary = function (doc) {

   

};

CP_PMIF.importFormats = function (doc, templateDoc) {

   

};

Notification (Constants.FA_Note_PostOpenMIF, true);

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

   

}

This is called the Namespace pattern. I am creating a single global object variable (CP_PMIF) that I give a unique name to. Now I add each of my functions to that global object, effectively "hiding" them from anything outside the object. Notice that all of my function calls have the object's name as a prefix (lines 12, 14, 18, etc.).

There are a couple of important things to note here: 1) Built-in FrameMaker functions, like the Notify function, still have to be at the root level of the script. They won't get called if you add them to the global object variable you defined. 2) Built-in FrameMaker functions and any other code outside of a function (like line 37) has to go at the bottom of the script, after all of the user-defined functions are added to the global object variable.

It takes a bit to get used to this approach, but I found it much safer and reliable, especially since I develop and test hundreds of scripts for different clients. Please let me know if you have any questions or comments.

Votes

Translate

Translate

Report

Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Advocate ,
Sep 01, 2016 Sep 01, 2016

Copy link to clipboard

Copied

Another way of doing this is enclosing your functions in a single uniquely named top-level function, or inside your own Notification handler:

function Notify ( iote, oObject, sParam, iParam )

{

     switch( iNote )

     {

          case Constants.FA_Note_PostOpenMIF : DoSomethingWithMIF( oObject ); break;

     }

     function DoSomethingWithMIF( oDoc )

     {

          /* more code here */

     }

     return;

}

This allows you to keep the function names simpler, without exposing them to the rest of the scripts in your session.

Votes

Translate

Translate

Report

Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Participant ,
Sep 01, 2016 Sep 01, 2016

Copy link to clipboard

Copied

Jang, Rick,

Thank you for your guidance, guys!

What if the function definition won't contain the argument, as follows:

function DoSomethingWithMIF( )

Instead of

function DoSomethingWithMIF( oDoc )

Does the presence of the argument inside parentheses really matter here?

Thanks again,

Roman

Votes

Translate

Translate

Report

Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Participant ,
Sep 01, 2016 Sep 01, 2016

Copy link to clipboard

Copied

Rick,

Thank you for your detailed explanations!

AFAIK, when you declare a global variable, it's some kind of a placeholder/prototype and its initial value in our case has no significance. Is it right?

Thanks!

Roman

Votes

Translate

Translate

Report

Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Community Expert ,
Sep 01, 2016 Sep 01, 2016

Copy link to clipboard

Copied

Its initial value is an empty object, indicated by the empty curly brackets {}.

Votes

Translate

Translate

Report

Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Participant ,
Sep 04, 2016 Sep 04, 2016

Copy link to clipboard

Copied

Hi guys,

I'd like the script to check whether the file from which all formats are imported is an appendix file (the 1st heading is of type "1 Heading Appendix"). So, I modified the script as follows:

...

var openRegFM = Open(regFileName, openParams, openReturnParams);

openRegFM.GetNamedPgfFmt = "1 Heading Appendix";

if ((openRegFM.ObjectValid () == 1) && (openRegFM.FirstPgfFmtInDoc == "1 Heading Appendix")) {

     opendoc.SimpleImportFormats(openRegFM, Constants.FF_UFF_COND|Constants.FF_UFF_FONT|Constants.FF_UFF_PAGE |Constants.FF_UFF_PGF|Constants.FF_UFF_REFPAGE|Constants.FF_UFF_TABLE|Constants.FF_UFF_VAR|Constants.FF_UFF_XREF);

                            }

...

However, for some reason that is unclear to me, this doesn't work. There are no errors produced, but the script seems to ignore the "if" part.

Any suggestions?

Thank you!

Votes

Translate

Translate

Report

Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Advocate ,
Sep 04, 2016 Sep 04, 2016

Copy link to clipboard

Copied

Hi Roman,

You are checking whether a paragraph format exists, not whether the first paragraph in the document has that format applied. If you are using the same template for all files, all files will have the same paragraph formats, hence the "if" to check whether the paragraph format exists will always yield 'true'. And when you check whether that paragraph format is the first format in the file, it will likely produce 'false', as it would be a strange coincidence if that paragraph format would be the first one in the list.

What you need to do to check the format of the first paragraph in the file is the following (I have made it into a function that can be used with any first paragraph format you might be looking for).

function CheckFirstParagraphFormat( oDoc, sFormat )

{

     var oFlow = oDoc.MainFlowInDoc;

     var oFrame = oFlow.FirstTextFrameInFlow;

     var oPgf = oFrame.FirstPgf;

     if( oPgf.Name == sFormat )

          return true;

     else

          return false;

}

The Name of the Pgf object is the name of the format that was applied to it.

Good luck

Jang

Votes

Translate

Translate

Report

Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Participant ,
Sep 05, 2016 Sep 05, 2016

Copy link to clipboard

Copied

Hi Jang,

Thank you for your input! The name of the property FirstPgfFmtInDoc misled me. I was sure it points to the 1st Pgf Fmt used in a doc. A newbie mistake. 🙂

BTW, I see you prefer to chunk the linked list into several variables. Isn't it preferable to just use one variable instead?

var FstPgf = oDoc.MainFlowInDoc.FirstTextFrameInFlow.FirstPgf

What if I'd like to find the next Pgf Name? AFAIK, there is no such thing as NextPgf in a text frame. Instead, we only have NextPgfInDoc and NextPgfInFlow. Unfortunately, the Object Reference doc. doesn't explain the difference between them.

Thank you!

Roman

Votes

Translate

Translate

Report

Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Advocate ,
Sep 05, 2016 Sep 05, 2016

Copy link to clipboard

Copied

LATEST

While debugging my scripts, I like to have each step available as a variable. Breaking up the single code line is just a habit. When I was coding for processors with 4K memory, I was concerned with memory space. Not anymore 🙂

About the NextPgf - there are various linked lists of paragraphs. You would likely be using NextPgfInFlow, as NextPgfInDoc may lead to paragraphs on the master or reference pages, or in untagged flows (background text defined on master pages). All your content should normally be in one single flow, which is the MainFlowInDoc.

Votes

Translate

Translate

Report

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