17 Replies Latest reply on Jan 14, 2017 7:15 PM by Justin Putney

    User-defined Objects as Arguments to app.doScript()

    Ed Little

      If I create my own classes in JavaScript for InDesign, I generally have no problem instantiating them, using them as arguments to functions, or returning them from functions.  If I push instances of my own classes into an arguments array that's passed to the app.doScript() function, InDesign seems to understand that there is some kind of object there, but it loses its ability to access the object's properties and functions. 

       

      To Illustrate:

      var theFoo = new Foo(42, 1701);

      var argsArray = new Array();

      argsArray.push(theFoo);

       

      alert("Calling runFoo()");

      runFoo(argsArray); //this works fine

      alert("Calling app.doScript()");

      app.doScript(runFoo, ScriptLanguage.JAVASCRIPT, argsArray, UndoModes.FAST_ENTIRE_SCRIPT, "Test"); //this throws

       

      function runFoo(aFooArray) {

           for (var i = 0; i < aFooArray.length; i++) {

               alert("Properties\nBar:  " + aFooArray[i].Bar + "\nBiz:  " + aFooArray[i].Biz);

               aFooArray[i].doSomething();

           }

      }

       

      function Foo(aBar, aBiz) {

           this.Bar = aBar;

           this.Biz = aBiz;

       

           this.doSomething = function() {

               alert("Doing something.");

           }

      }


       

      Has anyone seen this behavior?  Is there something I can do differently to preserve my objects and maintain the undo atomicity that the app.doScript() function provides?

        • 1. Re: User-defined Objects as Arguments to app.doScript()
          Marc Autret Level 4

          Hi Ed,

           

          The problem is that:

           

          app.doScript(runFoo, ScriptLanguage.JAVASCRIPT, argsArray, UndoModes.FAST_ENTIRE_SCRIPT, "Test");

           

          makes app.doScript to call runFoo() with no argument although you pass argsArray to the script. Since runFoo() expects an array and you send nothing, and since you do not deal with app.scriptArgs as an alternate argument provider, there is no chance your function work.

           

          Anyway you don't need to use script args in your example—although maybe in a more complicate one!— as you can directly work this way:

           

          app.doScript('runFoo(argsArray);', ScriptLanguage.JAVASCRIPT, undefined, UndoModes.FAST_ENTIRE_SCRIPT, "Test");

           

          @+

          Marc

          3 people found this helpful
          • 2. Re: User-defined Objects as Arguments to app.doScript()
            absqua Level 4

            Marc Autret wrote:

             

            ...Since runFoo() expects an array and you send nothing, and since you do not deal with app.scriptArgs as an alternate argument provider, there is no chance your function work...

             

            I don't think this is right. The OP's script should've worked; certain kinds of arguments—and not just custom objects, in my experience—don't make the jump across the doScript() call. This, for instance, works as it should:

             

            var argsArray = [42, 1701];
            
            app.doScript(runFoo, ScriptLanguage.JAVASCRIPT, argsArray, UndoModes.FAST_ENTIRE_SCRIPT, "Test");
            
            function runFoo(aFooArray) {
                 for (var i = 0; i < aFooArray.length; i++) {
                     alert(aFooArray[i]);
                 }
            }
            

             

            I've settled on using a string as the first argument to doScript(), as you suggest, but I'd like to understand what's going on here...

             

            Jeff

            1 person found this helpful
            • 3. Re: User-defined Objects as Arguments to app.doScript()
              Marc Autret Level 4

              Hi Jeff,

               

              You're right, my previous explanation is not relevant. It's not clear to me how script arguments are passed—I thought it was the job of the ScriptArg structure but indeed it's not the case… Sure there are people in the neighborhood who will clarify that point.

               

              @+

              Marc

              • 4. Re: User-defined Objects as Arguments to app.doScript()
                Harbs. Level 6

                I always use global variables when running doScripts(). I've always found it simpler than getting arguments just right...

                 

                Harbs

                • 5. Re: User-defined Objects as Arguments to app.doScript()
                  Ed Little Level 1

                  According to Adobe's documentation, the third argument to app.doScript() is an array of arguments to be passed to the script.  The question of why certain data types don't get passed to the script with all their properties and functions intact remains (for now) unanswered.

                   

                  The really, really good news is that your suggestion to forgo that particular mechanism and pass a string as the first argument to app.doScript() solves my problem!

                  • 6. Re: User-defined Objects as Arguments to app.doScript()
                    Marc Autret Level 4

                    Ed Little wrote:

                     

                    The really, really good news is that your suggestion to forgo that particular mechanism and pass a string as the first argument to app.doScript() solves my problem!

                     

                    Yes, app.doScript supports inline scripts as 1st argument and then is similar to eval(). Anyway, this does not solve the technical problem of passing full object structures to another script—I mean, another script file.

                     

                    1) Apparently the 3rd arg to app.doScript only supports an array of scalars (Number, Boolean, String), although you can also include Arrays, provided that each element is itself a scalar or an array of scalar (recursively). E.g.:

                     

                    app.doScript( myScript, undefined, [ true, "hello", [1,2,["aaa","bbb"]], 18 ] );

                     

                    But simple key-value pair objects won't pass through! More precisely, they are recovered as empty objects: ({})

                     

                    2) Contrary to what I thought the doScript's array of arguments doesn't seem connected to app.scriptArgs—which IMHO is as stupid as confusing! However the ScriptArg approach is not much more promising, as the ScriptArg's getters/setters only undertand strings as ultimate values—according to the doc.

                     

                    3) Finally, if one needs to pass a (simple) object to myScript, maybe the only way is to uneval the structure—or .toSource()—in order to pass a string and to recover the whole thing via eval() or a dedicated Function. Here is a proof of concept:

                     

                    function myScript(args)
                    {
                        // Extract the passed object
                        var o = (Function('return '+args[0]))();
                    
                        alert( o.foo ); // => 1
                        alert( o.bar ); // => "Bar"
                        o.doSth();        // -> alert a "hello" msg
                    }
                    
                    
                    var myObj = {
                        foo: 1,
                        bar: "Bar",
                        doSth: function(){alert("hello!");}
                        };
                    
                    app.doScript(myScript, ScriptLanguage.JAVASCRIPT, [myObj.toSource()]);
                    

                     

                    Now, what I don't know is how to make args available to a different script file (?)

                     

                    @+

                    Marc

                    • 7. Re: User-defined Objects as Arguments to app.doScript()
                      absqua Level 4

                      Marc Autret wrote:

                       

                      1) Apparently the 3rd arg to app.doScript only supports an array of scalars (Number, Boolean, String), although you can also include Arrays, provided that each element is itself a scalar or an array of scalar (recursively). E.g.:

                       

                      It'll support native InDesign objects too, at the top level:

                       

                      var doc = app.activeDocument,
                          pageItems = doc.pageItems.everyItem().getElements();
                      
                      app.doScript(main, ScriptLanguage.JAVASCRIPT, pageItems, UndoModes.FAST_ENTIRE_SCRIPT, "Test");
                      
                      function main(args) {
                          alert(args[0].id); // id
                      }
                      

                       

                      But not as an element in the arguments array:

                       

                      var doc = app.activeDocument,
                          pageItems = doc.pageItems.everyItem().getElements();
                      
                      app.doScript(main, ScriptLanguage.JAVASCRIPT, [pageItems], UndoModes.FAST_ENTIRE_SCRIPT, "Test");
                      
                      function main(args) {
                          alert(args[0][0].id); // error
                      }
                      
                      • 8. Re: User-defined Objects as Arguments to app.doScript()
                        Harbs. Level 6

                        Seriously. Why is it worth all this effort?

                         

                        Just use global variables and be done with it...

                        • 9. Re: User-defined Objects as Arguments to app.doScript()
                          Ed Little Level 1

                          I thought (only briefly) about that approach, but for readability and maintainability purposes I'd rather stay away from using global variables if another solution exists.

                           

                          http://stackoverflow.com/questions/484635/are-global-variables-bad

                          http://www.cs.usfca.edu/~wolber/courses/110/lectures/globals.htm

                          • 10. Re: User-defined Objects as Arguments to app.doScript()
                            Harbs. Level 6

                            Bah.

                             

                            Just make a single global object or function to store your globals and don't worry about theory. It's perfectly readable and maintainable.

                             

                            With InDesign scripting globals are pretty much a necessity of life. In fact, global functions can really improve performance in certain cases.

                            • 11. Re: User-defined Objects as Arguments to app.doScript()
                              Harbs. Level 6

                              Just make sure to always use kConstant for global contants and gVariable for global variables, and there's not much room for confusion...

                              • 12. Re: User-defined Objects as Arguments to app.doScript()
                                Harbs. Level 6

                                One example:

                                 

                                I always define kAppVersion = parseFloat(app.version);

                                 

                                I then use that global varaible probably dozens of times.

                                 

                                Is that bad? I don't think so. If you can avoid hitting the InDesign DOM, it's a good thing. Hitting the InDesign DOM is a whole lot worse than an idealogical aversion to global variables...

                                • 13. Re: User-defined Objects as Arguments to app.doScript()
                                  Ed Little Level 1

                                  Just about any good rule is made to be broken.  Your example (and probably plenty of others) makes a good example of when it might be useful to not follow the no-global-variable rule.  If my particular problem can be efficiently solved while still following the rule, that would be my preferred outcome.

                                  • 14. Re: User-defined Objects as Arguments to app.doScript()
                                    Harbs. Level 6

                                    To each his own.

                                     

                                    In my book, the bandwidth wasted on dealing with all the ideosyncracies of passing arguments to doScript is reason enough to break the rule -- a rule that's more often broken than not when scripting InDesign...

                                     

                                    In practice I rarely use doScript except to deal with UndoModes. In that case, just about the whole script is run from a single doScript where I just call "main" from the doScript...

                                    • 15. Re: User-defined Objects as Arguments to app.doScript()
                                      Marc Autret Level 4

                                      Harbs,

                                       

                                      I totally agree with you on breaking the rule—as long as I don't break my own one ;-)

                                       

                                      There are many circumstances where ExtendScript's globals are useful. Your example is probably the best one: storing environment or app data retrieved from the DOM (locale, version, OS, etc.). In addition, global objects and/or functions are essential to speed up the loading of a library (or any Singleton pattern) in a persistent engine. Nothing wrong with that.

                                       

                                      Anyway, I don't agree we are wasting time on “dealing with all the ideosyncracies of passing arguments to doScript.” This is not a waste of time trying to understand an obscure feature. This is how we progress in scripting.

                                       

                                      @+

                                      Marc

                                      • 16. Re: User-defined Objects as Arguments to app.doScript()
                                        Harbs. Level 6

                                        I'm not sure if you're trying to understand an obscure feature or an obscure bug, but whatever...

                                         

                                        Okay. Have fun. Don't let me be the party pooper.

                                         

                                        I have work to do...

                                         

                                        Harbs

                                        • 17. Re: User-defined Objects as Arguments to app.doScript()
                                          Justin Putney Level 2

                                          The arguments can easily choke if it's not an array of strings.

                                           

                                          The same is true of returning a value from a doScript.

                                           

                                          I find it works well to serialize everything as JSON.

                                           

                                          Harb's suggestion of a global variable is good too.