JSX.js A Game Changer in Adobe HTML Extensions Development?

TrevorAdobe Extensions11 Comments

JSX.js is a simple API to significantly speedup and simplify your extensions development.

The manifest.xml is the file that instructs the CEP engine about the setup of the extension, in which host applications should it be run, what should be the dimensions of the UI if any, etc. etc. and the location of the "index.html" and "host.jsx", well thus goes the manual. The problem with the manual's approach is that every time you make a change in the host.jsx you have to restart the application to see the change. Wouldn't it be nice if you could just at a click of a button and in an instance restart the extension so that all the changes in all the files including the jsx files are reflected? Well it's dead simple, at least when someone's done the tough stuff for you. Also wouldn't it be nice if executing jsx scripts from the js engine was made really simple without the need of "quoting" + every + "other" + word? Wouldn't it be nice if when we run our jsx scripts they can know there own path? If the answer to those question is yes then continue reading otherwise adios amigos
On some of the versions of some of the applications on Windows changes are reflected after closing and reopening the extension. Changes made in the manifest are not reflected, you need to restart the app for that one.

[Updated 16 April 2018 for JSX.js version 2] JSX.js is no longer dependant on NodeJS or csInterface.js
The error messages are more useful now, both the jsx.evalScript and jsx.evalFile methods provide detailed error messages.
I need to update this post but in the meantime look at the scripts annotations to see changes in the defaults :-|

Compare the following 2 manifest snippets.

manifest.xml (Typical as per manual)Copy
manifest.xml (Not typical as per Trevor)Copy

If you use the "Typical" method then 1) in order to have the extension reflect the changes you make you'll need to restart the host app and 2) you'll never really know what gets loaded first the js or the jsx (well maybe smarty pants out there does know). If you use the "Trevor" method then 1) you'll see the jsx changes reflected with a single click and 2) you'll know the order the 2 engines or run. The "Trevor" method misses out the line <ScriptPath>./jsx/myFab.jsx</ScriptPath> so what do we do instead? JSX.js in particular the jsx.evalFile() method.
JSX.js has 2 methods jsx.evalFile(file); for executing jsx snippets and jsx.evalScript(script); for executing jsx
files.
jsx.evalFile(file, callback, replacements, forceEval);

PARAMETERS:

The 4 parameters file callBack replacements and forceEval can be entered in any order. One can if one is interested for readability purposes use a single object, in which each parameter has multiple keys that can be used for then that can be either lowercase or camelCase.
One can also use the lowercase jsx.evalfile() or shorthand jsx.file() instead of the camelCase jsx.evalFile()

jsx.evalFile() parameters in any order or as objectCopy

file Type: String
Description: The path of the jsx file to be executed. Can be an absolute path or path relative to the
extensions folder or a file in the extensions jsx folder.

jsx.evalFile() file parameterCopy
callBack Type: Function [Optional Default: None]

Description: The callback function that receives the result from the jsx script.
The result will always be a string so you might need to eval it in the callback function.
See the not by the forceEval parameter on callbacks with InDesign.

jsx.evalFile() callback parameterCopy
replacements Type: Object [Optional Default: None]

Description: The replacements that you might want made. In the jsx file surround words you want
replaced with __ like __this__ and __that__ and then you make your replacement object where the keys are the
strings you want replace and the values are their replacements.

myFabScript.jsx template jsx scriptCopy
myOtherFabScript.jsx template jsx scriptCopy
jsx.evalFile() replacements parameterCopy
forceEval Type: Boolean [Optional Default: false]

Description: This wraps the script in an eval What for? The callback as mentioned above can only accept a String. All the apps pass the result of the script as a String except for InDesign unless the script explicitly returns a string. Let's compare the following 2 scripts. Script 1 (length 1 byte) 1, Script 2 (length 3 bytes) '1' Script 1 will in all the apps except for InDesign pass the 1 to the callback, in InDesign the callback will be triggered but the 1 will not be received In Script 2 even InDesign will will pass the 1 to the callback because it was explicitly passed as a string. 2 methods of non-explicitly forcing the result to be a string are 1) to run the script using $.evalFile() and 2) to wrap the script in an eval. By default the jsx.evalFile() method will execute the jsx script using $.evalFile() and therefore even InDesign will return the result as a String however when replacements are made then $.evalFile() is not used and therefore unless the result was explicitly set as a String InDesign will not receive the result. Setting the forceEval to true will wrap the script in an eval and therefore for the result to be a String Summary: Don't set forceEval to true unless you are 1) Using InDesign and 2) Using replacements and 3) Not explicitly passing the result as a string and 4) You need the result.

1.jsxCopy
hello.jsxCopy
math.jsxCopy
jsx.evalFile() forceEval parameterCopy

NOTE: When you execute the jsx script using the "Typical" manifest.xml method the
$.fileName will not return the file's path. When using jsx.evalFile() the correct Path will be returned unless replacements have passed. To return the correct path in all cases you can use $.__fileName('baseName') where baseName is the files baseName without the path. In most cases $.__fileName() without the baseName will also work, see below for the exceptions. If on a Mac you have a root folder and a volume of the same name, for example you have the root folder /myScripts and you have a volume /Volumes/myScripts and your script has the path /Volumes/myScripts/myScript.jsx then $.fileName will give the wrong path /myScripts/myScript.jsx. The correct path will be returned if you use $.__fileName('myScript.jsx'). If you just use $.__fileName() without the baseName then the same wrong path will be given as by the $.fileName property. In InDesign $.__fileName() without the baseName will only return the correct path if no other script has be executed using the jsx.evalFile() method after the current script has be executed. The other apps don't have this issue and all the apps including InDesign $.__fileName('myScript.jsx') will return the correct path. The only case where it won't is if you executed multiple jsx files using the jsx.evalFile() on files with different paths and the same baseName. So either avoid doing that or use $.__fileName() without the basename subject to it's limitations. $.__fileName('baseName') will only work for scripts executed using the jsx.evalFile() method. A new property $.__dirname will be available to all jsx scripts run in the extension's target engine as long as JSX.js loaded first.
RETURNS: Boolean
Description: Will return true if the js engine could process the jsx.evalFile() command and false
if it couldn't (like the file didn't exist). True will be returned even if the jsx engine cannot execute the script
(like if (5 = 3){2}).

jsx.evalScript(script, callback, replacements, forceEval);

PARAMETERS:

The 4 parameters script callBack replacements and forceEval can be entered in any order. One can if one is interested for
readability purposes use a single object, in which each parameter has multiple keys that can be used for them that can
be either lowercase or camelCase.
One can also use the lowercase jsx.evalscript() or the shorthand jsx.eval() or jsx.script() instead of the
camelCase jsx.evalScript()

jsx.evalScript() parameters in any order or as objectCopy

script Type: String
Description: The jsx script to be executed.
Like csInterface.evalScript() you need to escape \

jsx.evalScript() file parameterCopy
callBack Type: Function [Optional Default: None]

Description: The callback function that receives the result from the jsx script.
The result will always be a string so you might need to eval it in the callback function.
InDesign does not by default pass the result as a string so you need to either pass the result
explicitly as a string or set the forceEval to true if you need the result from InDesign, see more details
of this by the forceEval description.

jsx.evalScript() callback parameterCopy
replacements Type: Object [Optional Default: None]

Description: The replacements that you might want made. Surround words you want replaced with __ like
__this__ and __that__ and then you make your replacement object where the keys are the strings you want replace and
the values are their replacements.

jsx.evalScript() replacements parameterCopy

With ES6 Template literals, which should be available in CEP8, replacements should become much more simple even
using the csInterface.evalScript method, so the only advantage of using jsx.evalScript would be for the forceEval
argument when using with InDesign.

From CEP8 using ES6 Template literalsCopy
forceEval Type: Boolean [Optional Default: false]

Description: This wraps the script in an eval What for? The callback as mentioned above can only accept a String. All the apps pass the result of the script as a String except for InDesign unless the script explicitly returns a string. Let's compare the following 2 scripts. Script 1 (length 1 byte) 1, Script 2 (length 3 bytes) '1' Script 1 will in all the apps except for InDesign pass the 1 to the callback, in InDesign the callback will be triggered but the 1 will not be received In Script 2 even InDesign will will pass the 1 to the callback because it was explicitly passed as a string. Setting the forceEval to true will wrap the script in an eval and therefore force the result to be a String Summary: Don't set forceEval to true unless you are
1) Using InDesign and
2) You need the result.

jsx.evalScript() forceEval parameterCopy

Setting up the extension with JSX.js
How does one set up the extension so that one can restart the extension at a click of a button and all the changes in
both the js and jsx files will be reflected?

Include in the index.html file the following line (or something similarish) to add the button. When the scripts
development is done you can remove the line.

index.html – add Restart Extension buttonCopy

Include the following files. If you prefer you can use plain js instead of jQuery. ../js/buttons.js is the name I give
the js script for controlling th HTML UI, substitute with the name you use.

index.html – Include files Copy

In your main.js file you execute your host.jsx file

main.js – executing the host.jsxCopy

In your buttons.js (or whatever you call your file that deals with your UI functions) file include something like
this.

buttons.js – The script where you handle the index.html UICopy

Then when you click on your button the extension will restart including all the jsx files, slashing development time
See the annotation at the beginning of the script for cost and terms of use.
I want to make it clear the the jsx.evalFile is a very useful function which save restarting the app on many occurrences BUT you should be aware that there will be times when you will need to restart the app. One important consideration is to put the window.location.href = "../html/index.html"; early in the code.
JSX.js (Download)

11 Comments on “JSX.js A Game Changer in Adobe HTML Extensions Development?”

  1. Avatar

    This script is a blessing, it really ease my pain during this nightmare that is writing CEP extensions.
    Thank you so much Trevor. I would hug you for a whole minute if I could.

  2. Avatar

    Hi, I'm trying to test your example with parameters passing to jsx script, but no results.

    function displayName()
    {
        jsx.evalFile('name.jsx', {name: 'Fred', age: 97});
    }
    
    alert('Test');
    (function(name, age){
        alert('Hi my name is ' + name + ' I am ' + age + ' years old.');
    })(__name__, __age__);
    

    I'm getting "Test" alert, but nothing more. Any ideas what could happened? Btw, great plugin and thanks for creating it.

    1. Trevor

      The snippet I placed was not good it should end ('__name__', age);
      I've updated the snippet, nice to see someone tried it out!
      When a script doesn't act as it should you can debug it using a callback.

      jsx.evalFile('name.jsx', {name: 'Fred', age: 97}, alert);

      You can use $.writeln or whatever you want instead.

  3. Avatar

    Hi Trevor, thanks for developing JSX.js. I've started using to handle communication in my CEP extension. However, I'm seeing that the console is no longer available in the evalFile'd JSX. For instance, if I have

    //myFile.jsx
    alert(typeof(console))
    

    and calling thins in my index.html

        <a href="#" onclick="jsx.evalFile('myFile.jsx')">JSX.js alerting console</a> |
    

    I'm seeing the alert say "undefined".

    Is the console available elsewhere (like $.__console) or is it unaccessible due to the file being run from another thread by JSX.js?

    Thanks for any help.

    1. Trevor

      Make sure to include <script src="../js/JSX.js"></script> in the header or at least before you reference it in the onclick.

      If you are then try removing line 62 var jsx

      Report back if the above helps.

      I think I misunderstood you and that you want the CEPS js engines console to be available in the Extend Scripts jsx engine. That is not going to happen! It would be very easy to access the console by adding an event listener in the JS script "com.shawn.console" that forwards data from and event dispatched in the jsx script.

      Look at the source code of https://github.com/Trevor-/console
      In particular https://github.com/Trevor-/console/blob/master/com.creative-scripts.console/jsx/__log.jsx#L33-L59 and
      https://github.com/Trevor-/console/blob/master/com.creative-scripts.console/js/console.js#L125-L150

  4. Avatar

    Hi Trevor

    Thanks for your quick response. I am using CEP 6 with illustrator CC 2015, ver 19.0.0.

    Do you have any workable example host.jsx, please provide me. Any examples host.jsx much appreciated.

  5. Avatar

    Hi Trevor,

    Its really wonderful and useful article, only thing the host.jsx is not working in this, can you post some example.

    1. Trevor

      Hi VV

      The post includes quite a lot of examples already, I think you need to read the section "Setting up the extension with JSX.js" carefully. Basically you need to in the manifest.xml file not include

      ./jsx/host.jsx

      You also have to include the line

      --enable-nodejs

      see the start of the post as to where in the manifest file to include these. In index.html include the line given that you have placed the JSX.js file in the js folder. Place your host.jsx file in the jsx folder and then in your main.js call the host.jsx file using jsx.file('host.jsx', myCallBack); where myCallBack is a function that does something with the result. It could be myCallBack = function (result){ alert(result); }; or perhaps something more useful.

      HTH, Trevor

  6. Avatar

    Thank You Trevor,
    I can't wait to test it out. Specially for the replacements method.
    This all sounds very nice.
    Ben

    1. Trevor

      Hi Ben,

      Thanks for your comment. I've added a note by the replacement parameter of the jsx.evalScript method about replacements in CEP8 using template literals. You should see there.

Leave a Reply

Your email address will not be published. Required fields are marked *