20 Replies Latest reply on Sep 5, 2016 7:24 AM by 4everJang

    ES: Passing a function variable value to another function

    rombanks Level 1

      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!

        • 1. Re: ES: Passing a function variable value to another function
          4everJang Level 3

          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

          • 2. Re: ES: Passing a function variable value to another function
            Russ Ward Level 4

            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

            • 3. Re: ES: Passing a function variable value to another function
              rombanks Level 1

              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[i].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_VA R|Constants.FF_UFF_XREF);

                                          } 

                               }

                                        openRegFM.Close(true);

                          }        

                }

              }

               

              function getOpenPrefs() {

                  var params, i;

                 

                  params = GetOpenDefaultParams();

                 

                  i = GetPropIndex(params, Constants.FS_RefFileNotFound);

                  params[i].propVal.ival = Constants.FV_AllowAllRefFilesUnFindable;       

                  i = GetPropIndex(params, Constants.FS_FileIsOldVersion);

                  params[i].propVal.ival = Constants.FV_DoOK;

                  i = GetPropIndex(params, Constants.FS_FontChangedMetric);

                  params[i].propVal.ival = Constants.FV_DoOK;  

                  i = GetPropIndex(params, Constants.FS_FontNotFoundInCatalog);

                  params[i].propVal.ival = Constants.FV_DoOK;   

                  i = GetPropIndex(params, Constants.FS_FontNotFoundInDoc);

                  params[i].propVal.ival = Constants.FV_DoOK;   

                  i = GetPropIndex(params, Constants.FS_LockCantBeReset);

                  params[i].propVal.ival = Constants.FV_DoOK;       

                  i = GetPropIndex(params, Constants.FS_FileIsInUse);

                  params[i].propVal.ival = Constants.FV_OpenViewOnly;     

                  return (params);

              }

               

              Best regards,

              Roman

              • 4. Re: ES: Passing a function variable value to another function
                4everJang Level 3

                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.

                • 5. Re: ES: Passing a function variable value to another function
                  frameexpert Level 4

                  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.

                  • 6. Re: ES: Passing a function variable value to another function
                    rombanks Level 1

                    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! :-)

                    • 7. Re: ES: Passing a function variable value to another function
                      4everJang Level 3

                      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.

                      • 8. Re: ES: Passing a function variable value to another function
                        rombanks Level 1

                        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

                        • 9. Re: ES: Passing a function variable value to another function
                          4everJang Level 3

                          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.

                          • 10. Re: ES: Passing a function variable value to another function
                            frameexpert Level 4

                            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.

                            • 11. Re: ES: Passing a function variable value to another function
                              rombanks Level 1

                              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

                              • 12. Re: ES: Passing a function variable value to another function
                                frameexpert Level 4
                                #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.

                                • 13. Re: ES: Passing a function variable value to another function
                                  rombanks Level 1

                                  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

                                  • 14. Re: ES: Passing a function variable value to another function
                                    4everJang Level 3

                                    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.

                                    • 15. Re: ES: Passing a function variable value to another function
                                      frameexpert Level 4

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

                                      • 16. Re: ES: Passing a function variable value to another function
                                        rombanks Level 1

                                        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

                                        • 17. Re: ES: Passing a function variable value to another function
                                          rombanks Level 1

                                          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_VA R|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!

                                          • 18. Re: ES: Passing a function variable value to another function
                                            4everJang Level 3

                                            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

                                            • 19. Re: ES: Passing a function variable value to another function
                                              rombanks Level 1

                                              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

                                              • 20. Re: ES: Passing a function variable value to another function
                                                4everJang Level 3

                                                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.