14 Replies Latest reply on Nov 13, 2016 6:19 PM by sberic

    Getting returned values from evalScript calls

    sberic Level 2

      I have a few functions that return bits of information from the JSX context to the panel JS so that they can be further processed there. One such function returns an array of times-in-seconds (Array<number>). I have verified with console.log that I do indeed get the results back. However, while I was expecting an Array<number> in the callback, the type of the value passed to the callback is string!

       

      Is it possible to get objects in the callbacks or will everything simply be coerced to a string? If the latter, is there a suggested way to convert and then reconvert the data? (I looked but didn't see a JSON utility object/class in the JSX context to assist with this...)

        • 1. Re: Getting returned values from evalScript calls
          Thomas_Szabo Level 3

          There are a couple of ways to pass back arrays and objects from JSX but in almost all cases you have to convert your data to a string.

           

          1. manually "stringify" or convert your array to a string when returning it (my most preferred way).

          var myArray = ["butter", "milk", "cheese"];

          return '['+myArray.join(",")+']';

           

          2. use a 3rd party json lib. Dont rely on the built in JSON object!!

          For our After Effects extension we´re using this lib:

          GitHub - douglascrockford/JSON-js: JSON in JavaScript

           

          Put it in the same folder as your .jsx file and add this to your jsx:

          #include "json2.js"

           

          Now you can use good ol JSON.parse() and JSON.stringify() in JSX

           

          ------

           

          When the string arrives in HTML/JS land you simply convert it back to what it´s supposed to be. Make sure you wrap it in a try/catch because you don´t want unhandled errors to bother the user.

           

          csInterface.evalScript( "$.getMyArray()", function(array_string) {

               var myArray;

               try {

                    myArray = JSON.parse(array_string);

               } catch (error) {

                    console.log(error);

                    myArray = [];

               }

          });

           

          Have fun.

          Thomas

          • 2. Re: Getting returned values from evalScript calls
            sberic Level 2

            Thanks Thomas_Szabo for the great writeup! Very informational. I've already downloaded the JSON library you linked. I was planning on simply renaming it to a .jsx file and then including it into the project in the manifest. Any particular reason to not do it this way?

             

            Also, I did a quick test and found that there does appear to be a JSON utility built into the ExtendScript context (though it doesn't appear in the OMV?). With respect to this:

            Thomas_Szabo wrote:

             

            2. use a 3rd party json lib. Dont rely on the built in JSON object!!

            Why not use it? Are there issues with it? Are they documented somewhere?

            • 3. Re: Getting returned values from evalScript calls
              sberic Level 2

              I also did a little digging and found this sample script that came with the ExtendScript Toolkit:

               

              /Applications/Adobe ExtendScript Toolkit CC/SDK/Samples/javascript/SendObjectToPhotoshop.jsx

               

              In there they call a toSource() function on the data they wish to send across the fence. Here is what the OMV has to say about that function:

               

              This function serializes the object, so that it can, for example, be passed between engines. Pass the returned string back to eval() to recreate the object. Works only with built-in classes.

               

              I'm not sure what the limitation implied by "works only with built-in classes" is. Perhaps that it won't work with custom types you create yourself in C++? (Would love an official word on this!)

               

              Regardless, you can retrieve the object by calling eval() on the JS side of things. Has anyone used this method? Any known limitations?

              • 4. Re: Getting returned values from evalScript calls
                sberic Level 2

                Another option identified in this ongoing saga...

                 

                It appears that the JSX environment includes not one, but two JSON libraries. The Data Browser of the ExtendScript Toolkit shows JSON and JSON3 objects. A simple test showed that the results were identical (and perhaps they are in the back-end?), but they may act differently. A search for "JSON3 ExtendScript" revealed a connection to the official JSON3 library. No idea what version of JSON3 is included in the environment but it's there.

                 

                I'm now playing around with using JSON3 to stringify and then the browser's JSON.parse() function to get the object back on the JS side.

                 

                Thomas_Szabo, any thoughts on JSON versus JSON3 and why you wouldn't use either (or both)?

                • 5. Re: Getting returned values from evalScript calls
                  Thomas_Szabo Level 3
                  Why not use it? Are there issues with it? Are they documented somewhere?

                  Yes, it´s an object instantiated by a different panel. This JSON object is undefined in some occasions. That´s why it´s unreliable.

                  • 6. Re: Getting returned values from evalScript calls
                    sberic Level 2

                    Thomas_Szabo wrote:

                    Yes, it´s an object instantiated by a different panel. This JSON object is undefined in some occasions. That´s why it´s unreliable.

                    Wait, what? The JSON and JSON3 objects in question exist in the ExtendScript environment, not a panel. As for the JSON objects in JavaScript used by the panels, this is provided by CEF natively...

                     

                    Are there ExtendScript environments in other applications (outside of Premiere Pro) that for whatever reason don't have JSON and/or JSON3 in them?

                    • 7. Re: Getting returned values from evalScript calls
                      Thomas_Szabo Level 3

                      Thomas_Szabo, any thoughts on JSON versus JSON3 and why you wouldn't use either (or both)?

                      I don´t feel well relying on 3rd party libs in the JSX world. JSX is (what they call) Adobe-flavoured JavaScript. But it´s not JavaScript. Do a simple test and try to run setTimeout(). That function is undefined.

                      Although these 3rd party libs might be good, they are written for the Javascript world and not JSX! And even though there is a lot of overlap in both, there is a certain chance that a lib breaks your code execution because of language mismatch. JSX is not as forgiving as JS!

                      It´s smashing the user a nasty error popup in the face which is really bad UX. That´s why I prefer to have as much control over my code as possible and why I tend to do a manual stringify.

                       

                      Besides this I have no opinion over JSON vs JSON3. I haven´t used the latter yet.

                       

                      When I speak of JSON has been added by a different panel, I do speak of the JSX environment. The objects, functions and vars that you create when your panel is loaded along with your jsx file, are specific to your panel. So is the JSON object. Even if the creator panel is invisible and coded by Adobe itself. Please do not use the existing JSON object in the ExtendScript environment if it´s not created by you!!!

                      • 8. Re: Getting returned values from evalScript calls
                        sberic Level 2

                        I don´t feel well relying on 3rd party libs in the JSX world. JSX is (what they call) Adobe-flavoured JavaScript. But it´s not JavaScript. Do a simple test and try to run setTimeout(). That function is undefined.

                        My understanding is that it is "not JavaScript" in that it is specifically not Browser JavaScript. This is similar to the way that JavaScript written in a Node environment does not contain the document global variable (or even the Document class, for that matter). In fact, it appears that ExtendScript equates to the 3rd Edition of ECMA-262 Standard (with some extra goodies). More specifically, ExtendScript is an implementation of ECMAScript. In relation to this point, that Wikipedia article has the following point:

                        [A]pplications written in one implementation may be incompatible with another, unless they are written to use only a common subset of supported features and APIs.

                        The setTimeout() example would appear to be one such API that is not supported in the ExtendScript environment. Third-party libraries that target JavaScript v1.5 have an okay chance of working, though it would be best, of course, to be extremely careful here. By the sound of it, you've vetted the json2.js file already.

                         

                        My point is that the JSON and JSON3 global objects appear to be supplied by Adobe in the ExtendScript environment itself by default, much the same way the global variables app and $ magically exist for your use in ExtendScript environments. You can verify this yourself by checking out the ExtendScript Toolkit's Data Browser when you connect to an application with an empty JSX script. Scroll down a bit and you'll find those two objects alongside a mysterious JSONBlocksToJSONArray() function. No idea what that is but it's there.

                         

                        The objects, functions and vars that you create when your panel is loaded along with your jsx file, are specific to your panel.

                        The objects, functions, and vars that you create in the panel context are specific to your panel, but does the Application (e.g. Premiere Pro) also create a "per-panel" ExtendScript environment instance as well? I.e. if I created two panels and fed each their own JSX file with unique functions defined in the global scope, I would not be able to call a function defined in one context from the other, yes?

                        So is the JSON object. Even if the creator panel is invisible and coded by Adobe itself. Please do not use the existing JSON object in the ExtendScript environment if it´s not created by you!!!

                        I don't follow this part. The JSON and JSON3 objects are built-in and supplied by Adobe. They both contain parse() and stringify() functions that appear to do exactly what you'd expect. It really does seem like they exist for this purpose...

                         

                        Bruce Bullis, care to weigh in here with an insider's view? What are the JSON and JSON3 global variables? What's the difference? Why are there two?

                        • 9. Re: Getting returned values from evalScript calls
                          Bruce Bullis Adobe Employee

                          I'll ask CEP for a distinction between the two.


                          I haven't tested recently, but I believe the JSON object is published not by PPro's CEP implementation, but by an Adobe-installed panel. It should always be available, but I don't think it's up to PPro whether it is always available.

                           

                          I endorse Thomas's caution, and suggest using JSON in your JavaScript, whenever possible.

                          • 10. Re: Getting returned values from evalScript calls
                            sberic Level 2

                            I haven't tested recently, but I believe the JSON object is published not by PPro's CEP implementation, but by an Adobe-installed panel. It should always be available, but I don't think it's up to PPro whether it is always available.

                            If an Adobe-installed panel runs first and inserts an object into the ExtendScript global namespace and it is then usable by a third-party (our) panel's JSX code, that would argue that all panels share a single ExtendScript context, right?

                             

                            I hadn't realized before that Adobe supplies some default panels. I found these three in /Library/Application Support/Adobe/CEP/extensions:

                            1. CC_LIBRARIES_PANEL_EXTENSION_2_3_1050
                            2. CC_LIBRARIES_PANEL_EXTENSION_2_6_64
                            3. CCX_START_EXTENSION_1_2_1_001

                            I also found five panels in /Applications/Adobe Premiere Pro CC 2017/Adobe Premiere Pro CC 2017.app/Contents/CEP/extensions:

                            1. com.adobe.DesignLibraries.angular.2015.1
                            2. com.adobe.URLAnchorExtract.extension
                            3. com.adobe.ccx.start
                            4. com.adobe.susi-dva.extension
                            5. com.adobe.susi.extension

                             

                            It appears as though several of the panels above include a json2.jsx file, which would define the global JSON object. Two of them also hold references to a JSON3 object. The one that appears most interesting is in the com.adobe.ccx.start panel's ccx-start.jsx file. It appears in a section with the following comment:

                            **
                             * Premiere Pro Host ExtendScript Interface for CCX extensions
                             */
                            CCXWelcomeXSHost_PPRO = {
                            

                             

                            The file appears to include similar versions for other Host applications. Further, checking the Data Browser shows that several of these do indeed exist.

                             

                            So this somewhat raises an interesting question: is this the very purpose of the com.adobe.ccx.start panel? To inject useful libraries such as JSON3 into ExtendScript for extensions to use as the comment indicates?

                             

                            Further, do panels communicate with a shared ExtendScript context or do they each get their own instance? One that perhaps had some initialization run by a separate "bootstrap" built-in panel first?

                            • 11. Re: Getting returned values from evalScript calls
                              Bruce Bullis Adobe Employee

                              Panels share an ExtendScript context, yes.

                               

                              The Start panel's primary purpose is to serve as our 'Hello' dialog.

                               

                              Panels can communicate however they wish; the PProPanel sample shows how to send/receive custom CSXS events (across ExtendScript/JavaScript), and I've just added a (JavaScript-based) Vulcan conversation between PProPanel and AfterEffectsSample; check-in soon.

                               

                              Panels don't provide contexts for other panels; they're all CEP panels. One extension can contain several panels (often, modal 'settings' dialogs), and CEP HTML Test Extension shows how to enumerate, open and close other panels.

                               

                              Some Adobe panels provide functions; these were never designed or tested for third-party use, which is why I encourage third parties to use JSON at the JavaScript (CEP) layer.

                               

                              Hope that makes sense.

                              • 12. Re: Getting returned values from evalScript calls
                                sberic Level 2

                                The Start panel's primary purpose is to serve as our 'Hello' dialog.

                                I didn't understand this until I temporarily removed all of the built-in extensions. This is the window that you're presented with when you open Premiere Pro. See:

                                Screen Shot 2016-11-11 at 10.41.09 AM.png

                                the PProPanel sample shows how to send/receive custom CSXS events (across ExtendScript/JavaScript)

                                Sending messages from Panel→ExtendScript is outlined in the documentation as well. What isn't explicitly stated is that messages (including returned results) are coerced into strings. If you pass a single number, JavaScript is "kind" enough to coerce it back when you attempt to use it in a context expecting a number. However, return an Array and you may be initially surprised.

                                 

                                Some Adobe panels provide functions; these were never designed or tested for third-party use, which is why I encourage third parties to use JSON at the JavaScript (CEP) layer.

                                I now understand that some Adobe panels provide functions and "install" objects into the shared ExtendScript context. That is a huge conceptual win that I've received from this conversation.

                                 

                                However, I'm a little confused about the terminology used here: "I encourage third parties to use JSON at the JavaScript (CEP) layer." Based on the language in the CEP 6.1 HTML Extension Handbook, this is referring specifically to the JavaScript environment embedded in panels: that provided by the CEF implementation that supports ECMAScript 5. In this case JSON is 100% built-in. It's simply always there and does not require a special library. Cool.

                                 

                                What's being discussed here is how to convert an object, array, etc. into a JSON string within the ExtendScript context so that it may be passed back to the calling panel for parsing there. It seems that Adobe's built-in panels supply their own JSON libraries (whether json2.js or JSON3) for this purpose. The json2.js file is typically renamed to json2.jsx whereas the JSON3 script is copied into a larger .js or .jsx file.

                                 

                                One thing I'd like to point out is that the term "JavaScript" is rather overloaded here and it can easily become confusing as to which is being referenced. From the documentation:

                                There are two separate JavaScript engines here.

                                • JavaScript engine of host application - Application DOM/Extend script DOM
                                • JavaScript engine of CEP HTML runtime - HTML DOM

                                Application DOM is not available in CEP extension's engine and CEP DOM is not available in

                                host application's engine.

                                 

                                What isn't mentioned is that the two JavaScripts above are actually different versions of ECMAScript. The first one above is ECMAScript 3 and the second is ECMAScript 5 (which probably also supports JavaScript 1.8.5). Calling the first of these "JavaScript" sets up incorrect expectations, e.g. "setTimeout" should exist. (It doesn't because it's not defined in ECMAScript 3 [at least].)

                                 

                                Long story short, it sounds like panels should prepare a JSON library for the ExtendScript context if they want to pass complex objects back in returned results from calls to evalScript().

                                • 13. Re: Getting returned values from evalScript calls
                                  sberic Level 2

                                  The discussion has lots of details about the two environments and how they interact. The short answers to the questions are:

                                  Is it possible to get objects in the callbacks or will everything simply be coerced to a string?

                                  Everything is coerced into a string.

                                   

                                  Is there a suggested way to convert and then reconvert the data?

                                  Include your own JSON package in the JSX context. These two options are used by built-in panels (those built by Adobe) so they should work fine:

                                  1. JSON
                                  2. JSON 3

                                  Both of the above packages provide access to JSON.parse() and JSON.stringify().