Copy link to clipboard
Copied
I'll try to simplify this and add more detail if questions are asked:
I have two scripts: Script_A.jsx and Script_B.jsx.
Script_A.jsx creates a window and a button on the window runs:
#include "Script_B.jsx"
That works fine, but I would like to pass the window from Script_A to Script_B so Script_B can check some of the values from the window.
Is there a way to do this?
Something like:
#include "Script_B(win).jsx"
or
#include "Script_B.jsx {arguments:win}"
I realize it could be done by including the functions of Script_B into Script_A and making one massive script, but I'd like to make that my last resort.
Thanks in advance!
You could run both the scripts in the same engine, something like
#targetengine "main"
The you can set the variable you want to pass as a global variable and that will be available to script B.
Another approach could be to use the doScript method to call script B instead of #include. This method takes an array as an argument to pass to the calling script
-Manan
Disregard, I just ran a test and if I call Script_B from Script_A using the include statement, all of the Scrpt_A variables and values are available to Script_B, so I don't need to pass it a variable.
Copy link to clipboard
Copied
You could run both the scripts in the same engine, something like
#targetengine "main"
The you can set the variable you want to pass as a global variable and that will be available to script B.
Another approach could be to use the doScript method to call script B instead of #include. This method takes an array as an argument to pass to the calling script
-Manan
Copy link to clipboard
Copied
Hi @Marshall_Brooks you seem to be wanting to separate the UI (the window) from the script, which is an excellent idea. However, to do this, there are much easier ways than passing the window to a separate script file. One way that I commonly use is to handle the window creation and display (ie. the script's UI) in a function, and pass that function an object—I usually use a `settings` object containing all the properties the script needs.
You can see an example here. This post might be worth a bit of study because the OP's script has no separation of UI, and I post a version of the script which has the UI completely de-coupled, meaning that with a single "showUI = false" the script will run completely fine with no UI at all, using the properties already set in the `settings` object.
I also use #include files all the time—I store functions in the include file, and then call them from the main script. For a project I may have many many functions in such a "library" script file.
- Mark
Copy link to clipboard
Copied
Thanks! I had not heard of doScript before, and I'm not necessarily trying to separate the window from the script. Also I found this thread when I was researching DoScript: https://community.adobe.com/t5/indesign-discussions/how-do-i-make-a-script-both-executable-on-its-ow...
Let me share more info on what I am trying to do!!
Script A and Script B were each originally developed independently.
Script A presents a window which has about 15 checkboxes. 10 of the checkboxes can be selected simultaneously and are common to checkboxes on Script B. (For discussion, lets call them Ford, Chevy, Honda, Mazda, Nissan).
Script B presents a different window (they both might be named win, which presents a problem). Script B's window presents other checkboxes, 10 of them are common to Script A (Ford, Chevy, Honda, Mazda, etc.), but only one can be selected at a time.
What I want to achieve is if the user has Chevy (and only Chevy) checked on Scirpt A and clicks the Script B button, which fires the #include statement to launch Script B, the Chevy checkbox is clicked when Script B opens.
Ways that I can see to achieve this - but there might be better solutions and I could be wrong - in order of what I understand:
Copy link to clipboard
Copied
Disregard, I just ran a test and if I call Script_B from Script_A using the include statement, all of the Scrpt_A variables and values are available to Script_B, so I don't need to pass it a variable.
Copy link to clipboard
Copied
Right so basically if two scripts use the same engine they will share the execution context and hence the variables would be shared. This is what I suggested before
-Manan
Copy link to clipboard
Copied
And isn't the point / how include works? Doesn't it include complete source code of the imported script?
Copy link to clipboard
Copied
Yes it should import the whole code. However, it does help in modularising the code into different files and that is what the OP is after I think
-Manan
Copy link to clipboard
Copied
Correct, I'm using the #include statements to modularize and isolate the code, but it is from a practical standpoint and not an idealistic standpoint.
Basically, I like #include statements for three reasons:
EDIT: Example: Checkboxes have .values, but EditText fields have .text. If I were coding in VBA, using something like "etext.value==="This text" (aside from the triple = ) would product either a syntax error or a type mismatch. In JS, etext.value is always undefined, so the comparison always resolves to false and your script runs but you don't get the expected result.
EDIT2: Another example. In one of my scripts, I wanted to deselect a check box. The way you do that is "ckbx_SelectAll.value = false;". I put "ckbx_SelectAll = false;" Visual Basic would have highlighted that, probably shown a syntax error. Most like not run the script at all until it was fixed. Javascript just says "I don't know what this means … Oh well, must not be important … I'll just ignore it … Have a nice day!!!"
Copy link to clipboard
Copied
I do have a neophyte question and this thread is probably as good as any place to ask it in!
For background, I'm using Extendscript/ScriptUI to display pop-up windows and manipulate FrameMaker documents. I'm not totally clear on the distinction between the two, but that's not the question. ScriptUI is supposed to be universal among Adobe apps (InDesign, LifeCycle, Photoshop, FrameMaker) - although it behaves somewhat differently in InDesign than it does in FM than it does in ExtendScript Toolkit CC, whereas I think Extendscript is unique to FrameMaker.
As I said above, my background is in VBA, so I tend to "think" in terms of VBA and translate the syntax into Javascirpt/ScriptUI/Extendscript, if that makes sense. (I think ScriptUI also supports VB, but virtually nobody uses it that way, so it's easy to ask questions and work in JS).
Question background:
For discussion purposes, I have two templates: Template A and Template B.
I have two scripts: Script A and Script B.
Script A displays a window (panel) to modify user variables in Template A.
Script B displays a window (panel) to modify user variables in Template B.
The scripts were intended to work independently - i.e. you would open Template A, run Script A, close Script A, open Template B, run Script B, close Script B. Predictably, they work fine that way.
Script B was written from Script A so they use the same syntax - i.e. the pop-up is called "win" in both scripts. Both scripts use "var doc=app.ActiveDoc" etc.
Question:
I would expect the scripts to conflict if run simultaneously - i.e. they use the same variable names and definitions for different objects. In VBA, having win defined twice would probably lead to Duplicate Definition in the Current Scope. compile error. I would expect Script A to try to modify Template B if I had that active. The scripts didn't seem to do that. They both worked as expected. Script B modified Template B even if Template A were active. Clicking buttons on the Script A window didn't confuse the Script B window, etc.
Could someone explain how and why this happens?
Copy link to clipboard
Copied
I'm not JS guy - but i think it has something to do with "#targetengine" - scope of where script is run.
Copy link to clipboard
Copied
Copy link to clipboard
Copied
Copy link to clipboard
Copied
@Robert at ID-Tasker Some of the StackOverflow stuff is incorrect. #TargetEngine works in FrameMaker. I haven't used it, but if I specify #TargetEngine FrameMaker and double-click my .jsx file, I get a prompt asking if I want to run the script in FM. If I don't specify #TargetEngine, double-clicking opens the script in ESTK tool kit and I have to select FrameMaker there to run it in FrameMaker. But I typically don't specify a target engine in my scripts and they both could be running at the same time.
From what I can see (but this is all empirical):
Off-topic - 72K lines of code in my current VBA project, but I tend to not try to brag about code length. In my case, it is usually b/c I found something that could work with minor changes and repeated it rather than building it into a subroutine that I could call repeatedly.
Copy link to clipboard
Copied
Are those 72k lines InDesign related?
I don't duplicate code - just rewrite, add extra arguments, to make it more universal. And pretty much zero comments 😉
I've sent you a private message.
Copy link to clipboard
Copied
Somehow I missed this post. 72k lines are Visual Basic for Microsoft Access database. I don't even have InDesign, but I was told this was the best forum for ScriptUI questions.
Copy link to clipboard
Copied
I have a new issue related to this:
From the previous example:
For discussion purposes, I have two templates: Template A and Template B.
I have two scripts: Script A and Script B.
Script A displays a window (panel) to modify user variables in Template A.
Script B displays a window (panel) to modify user variables in Template B.
I also have a Master Script that checks a variable in the document and calls either Script A or Script B.
What I want to do is have Script A call the Master Script if the active document changes - but since the MasterScript include Script A, I get a Stack Overrun.
How would I avoid this?
I tried using app.doScript("MasterScript.jsx") and I get an error that app.doScript is not an approved function.
Note that I get the error when the function first opens, before that line of code executes, so I can't exit the function to avoid the error.
Pseudo-Code:
MasterScript.jsx:
main();
function main()
{
var doc = app.ActiveDoc;
if(!doc.ObjectValid())
{
alert("No active document. Cannot continue.");
return;
}
#include "Close_All_Palettes.jsx"
var VarExists_MST = false;
var varFmt;
varFmt = doc.GetNamedVarFmt("_zz_Template_Type")
if (varFmt.ObjectValid() )
{
VarExists_MST =true;
if (varFmt.Fmt ==="Doc A")
{
#include "Script A.jsx";
// return;
}
else if (varFmt.Fmt === "Doc B")
{
#include "Script B.jsx";
// return;
}
}
Script A Excerpt:
var doc = app.ActiveDoc;
CreateMainMenu(doc);
// end Main program
function CreateMainMenu(doc)
{
if (!doc.ObjectValid())
{
alert("No active document. Cannot continue.");
return;
}
var InitialDocName = doc.Name;
// ...
doc = app.ActiveDoc;
if (!VerifyTemplate(doc, InitialDocName))
{
return;}
}
}
function VerifyTemplate(doc, InitialDocName)
{
var TemplateValid = false;
if (doc.Name===InitialDocName)
{
TemplateValid = true;
}
else
{
var varFmt;
varFmt = doc.GetNamedVarFmt("_zz_Template_Type")
if (varFmt.ObjectValid())
{
if (varFmt.Fmt === "Doc A") {
alert("Active Document Changed. Re-opening Window. Please Re-click your selection.")
Win_Reopen();
}
else
{
// Below gives Stack Overun
#include "MasterScript.jsx"
// Below gives "app.doScript is not a function.
app.doScript("MasterScript.jsx");
//Below works, but I'd prefer something automated:
alert("Active Document Changed. Please run Master Script.)
}
}
}
return TemplateValid;
}
function Win_Reopen()
{
var doc = app.ActiveDoc;
CreateMainMenu(doc);
}
Copy link to clipboard
Copied
I can get around this with a Switch statement - i.e. instead of:
if (varFmt.Fmt === "Doc A") {
alert("Active Document Changed. Re-opening Window. Please Re-click your selection.")
Win_Reopen();
}
else
{
// Below gives Stack Overun
#include "MasterScript.jsx"
}
Change to:
switch (varFmt.Fmt)
{
case "Doc A":
alert("Active Document Changed. Re-opening Window. Please Re-click your selection.")
Win_Reopen();
break;
case "Doc B":
#include "Script_B.jsx";
break;
case "Doc C";
#include "Script_C.jsx";
}
Not nearly as elegant, but it works!!!
Copy link to clipboard
Copied
Aside: I'm actually shocked that the #includes in your switch code above actually work! It does work though—I tested it in simplified form. I always thought those directives were evaluated before the code is run, but your code proves this is not the case. 🤯
But here's my opinion. I strongly prefer to use included script files to load or instantiate objects, rather to perform actual operations. So my include files are like this:
/** @file MyWindows.js */
function MyWindowA(settings) {
var w = new Window ( /* ... */ )
// ...
// return the window closed value
return w.show();
function only_MyWindowA_Needs_Me () {
// ... do something here that only MyWindowA needs
};
};
function MyWindowB(settings) {
var w = new Window ( /* ... */ )
// ...
// return the window closed value
return w.show();
function only_MyWindowB_Needs_Me () {
// ... do something here that only MyWindowB needs
};
};
function i_Am_Used_By_Both_MyWindows() {
// ... do something here that both MyWindows want
};
function populateListBox(listbox, data) {
// ... example of a function used by both MyWindows
};
It defines a bunch of functions that I will then invoke in my actual script (or in other functions in other include files!). The thing is, if you run this script file, it will do nothing, because all it does is define functions.
Personally I think this is a much safer and cleaner approach to using multi-file scripts. Just my opinion.
- Mark
Copy link to clipboard
Copied
What I'm finding is that pretty much anything scriptUI essentially becomes a global property, so everything needs a unique name - i.e.
unique variables
unique controls
unique window names
unique function names
...
The exception being functions that are generic and repeated word-for-word in multiple procedures.
Copy link to clipboard
Copied
You *are* defining all your variables in the global space but it has nothing to do with ScriptUI. It is just what you are doing.
(It is also bad programming. I think maybe you don't know how to *not* define all your variables in the global space?)
The answer is: define almost everything inside functions—because ExtendScript scopes by function, not blocks like modern Javascript. You can then control which functions have access to which variables. This can avoid problems of exactly the sort that this entire thread is presenting.
It is too long a job for me to explain variable scoping in detail, but one approach you could take it to go to my profile and look at any script I have posted in the last 3 years and try to look at the structures I use. For Indesign I usually put all my code inside a main() function or sometimes an IIFE. I can then define functions inside or outside of that, depending on where I want them. In the global scope I only have #includes (which only define functions and never perform actual operations) and a single call to the main() function.
- Mark
Copy link to clipboard
Copied
What I found in another thread is that when you use an #include (which would be a javascript which contains functions), those functions become global, so to have your functions not be global, essentially, you would have to have one huge script instead of medium sized scripts with include functions ...
Copy link to clipboard
Copied
Hi @Marshall_Brooks , I'm not sure if this helps—ignore if it doesn’t. The #include file could have any number of public functions, constants, and variables, which may or may not be run from your script.
So this MyLibrary.jsxinc example has 2 public functions:
/**
* Simple PDF Prest dialog
* @ return the chosen dialog
*/
function exportPDFDialog(){
var pp;
var theDialog = app.dialogs.add({name:"Choose a Preset", canCancel:true});
with(theDialog.dialogColumns.add()){
pp = dropdowns.add({stringList:app.pdfExportPresets.everyItem().name, selectedIndex:3, minWidth:80});
}
if(theDialog.show() == true){
pp = app.pdfExportPresets.item(pp.selectedIndex);
theDialog.destroy();
return pp;
}
}
/**
* Makes a new named Layer
* @ param the document to add the layer to
* @ param new layer name
* @ return the new layer
*/
function makeLayer(d, n){
if (d.layers.itemByName(n).isValid) {
return d.layers.itemByName(n);
} else {
return d.layers.add({name:n});
}
}
/**
* A read only constant
*/
const MAGIC_NUMBER = 1.045;
/**
* A read write variable
*/
var vNum = 5
I can include and run them from a jsx file including a dialog function which returns the results:
#include "MyLibrary.jsxinc";
//run the exportPDFDialog() function from MyLibrary.jsxinc
alert("Chosen Export Preset: " + exportPDFDialog().name)
//run the makeLayer() function from MyLibrary.jsxinc
var ns = makeLayer(app.activeDocument, "MyNewLayer");
alert("New Layer Named: " + ns.name);
On run:
Copy link to clipboard
Copied
Ran into a problem and now I'm not sure how to resolve it.
As I said above:
In Script A:
switch (varFmt.Fmt)
{
case "Doc A":
alert("Active Document Changed. Re-opening Window. Please Re-click your selection.")
Win_Reopen();
break;
case "Doc B":
#include "Script_B.jsx";
break;
case "Doc C";
#include "Script_C.jsx";
}
Works!
But when I create Script B with:
switch (varFmt.Fmt)
{
case "Doc A":
#include "Script_A.jsx"
break;
case "Doc B":
// #include "Script_B.jsx";
alert("Active Document Changed. Re-opening Window. Please Re-click your selection.")
Win_Reopen();
break;
case "Doc C";
#include "Script_C.jsx";
}
If I try to run Script_B, it highlights the "#include Script_B.jsx" line in the Script_A.jsx file and gives a Stack Overrun error.
How do I avoid this?
Copy link to clipboard
Copied
@Marshall_Brooks I'm sorry to say that you are creating a horrible architecture. I strongly suggest you put your overall control code into the main script and include (at the start of the script—not using predicate logic) just library functions and objects.
As for the stack overrun, have a look in the debugger and see which function is filling the stack. For example I created a stack overrun by making a function call another function which then called the other function back...
- Mark
Find more inspiration, events, and resources on the new Adobe Community
Explore Now