The entire script must be passed in as a string to BridgeTalk, this is true even if you are using the script in Illustrator to talk only to Illustrator. You may be able to get the string of your Myscript script and bake it into the bt.body portion inside the scriptui event listener.
I created this script to do some exploration and I found some interesting results. I wanted to demonstrate the pattern of using one script to send to itself, along with two arguments: the name of a specific function inside the main function and an optional argument. First I made the pallette window as usual and re-wrote for the 100th time a BT-calling function in a different way from some previous times. When I got the two buttons working and creating either a circle or square with hard-coded arguments, I wanted to make a simulation of how a CEP evalScript call could trigger a method in the script. I knew that using Window.find it was possible to get a reference to a SUI 'window' in Illustrator and it was probably possible to even reach some properties which were added to it previously via custom code. In the bottom of the dialog function I stuck in a property which contains the methods available for use within this script. However, I made a special different set of methods to be called externally because while the buttons are meant to demonstrate a controlled higer-level control over lower-level methods within the UI; external calls I wanted to ensure would be used with dynamic arguments. Using a for-in loop was disastrous as for some reason my Window.find call managed to always determine that my method of choice was 'makeACircle' while I was plainly sending in .makeASquare(). Even though I was not even using BT but rather calling the methods directly and it was miraculously working, I also wanted to record the result in the textbox on the pallette. And since this was done in an onResult method, at that point I created new methods 'callMakeACircle' and 'callMakeASquare' which wrapped the original BT-using onClick handlers, switched the onClick handlers to call the methods 'makeASquare' and 'makeACircle' with the hard-coded arguments and it worked! But, why, and should it have worked? As simply using Window.find technique was launching the commands and affecting the document, couldn't we skip the whole BT thing anyway and have the onClick handlers do Window.find() to bypass it? It turns out that no, it cannot be skipped. The code running in this script must have a BT-enabled document-manipulation method running in UI control handlers. Running from an external JSX file (which is executing within Adobe Illustrator) and doing Window.find makes sense because this was a command launched not from the SUI palette but the back-end ExtendScript. And now this brings me to one point, which is, why not just use CEP for the UI and redo all SUI things using browser HTML and javascript? No matter how complex the SUI is, normal HTML and things are much faster to make a really robust UI with. There's a million frameworks for HTML and CSS, but if you just find something basic and work with Flexbox for css positions, it's kind of like SUI. You can make containers that go row/column for orientation but unlike SUI you can make this change based on how wide the view gets as people resize your CEP panel. Otherwise it's really doing more data transferring and executing in all these different places while CEP can just do evalScript and the UI is more reliable than our SUI.
//@target illustrator
//@targetengine main
function BTSample () {
"object"!=typeof JSON&&(JSON={}),function(){"use strict";function f(t){return 10>t?"0"+t:t}function quote(t){
return escapable.lastIndex=0,escapable.test(t)?'"'+t.replace(escapable,function(t){var e=meta[t];
return"string"==typeof e?e:"\\u"+("0000"+t.charCodeAt(0).toString(16)).slice(-4)})+'"':'"'+t+'"'}
function str(t,e){var n,r,o,f,u,i=gap,p=e[t];switch(p&&"object"==typeof p&&"function"==typeof p.toJSON&&(p=p.toJSON(t)),
"function"==typeof rep&&(p=rep.call(e,t,p)),typeof p){case"string":return quote(p);case"number":return isFinite(p)?String(p):"null";
case"boolean":case"null":return String(p);case"object":if(!p)return"null";if(gap+=indent,u=[],"[object Array]"===Object.prototype.toString.apply(p)){
for(f=p.length,n=0;f>n;n+=1)u[n]=str(n,p)||"null";return o=0===u.length?"[]":gap?"[\n"+gap+u.join(",\n"+gap)+"\n"+i+"]":"["+u.join(",")+"]",gap=i,o}
if(rep&&"object"==typeof rep)for(f=rep.length,n=0;f>n;n+=1)"string"==typeof rep[n]&&(r=rep[n],o=str(r,p),o&&u.push(quote(r)+(gap?": ":":")+o));
else for(r in p)Object.prototype.hasOwnProperty.call(p,r)&&(o=str(r,p),o&&u.push(quote(r)+(gap?": ":":")+o));return o=0===u.length?"{}":gap?"{\n"+gap+
u.join(",\n"+gap)+"\n"+i+"}":"{"+u.join(",")+"}",gap=i,o}}"function"!=typeof Date.prototype.toJSON&&(Date.prototype.toJSON=function(){
return isFinite(this.valueOf())?this.getUTCFullYear()+"-"+f(this.getUTCMonth()+1)+"-"+f(this.getUTCDate())+"T"+f(this.getUTCHours())+":"+
f(this.getUTCMinutes())+":"+f(this.getUTCSeconds())+"Z":null},String.prototype.toJSON=Number.prototype.toJSON=Boolean.prototype.toJSON=function(){
return this.valueOf()});var cx,escapable,gap,indent,meta,rep;"function"!=typeof JSON.stringify&&
(escapable=/[\\\"\x00-\x1f\x7f-\x9f\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g,
meta={"\b":"\\b"," ":"\\t","\n":"\\n","\f":"\\f","\r":"\\r",'"':'\\"',"\\":"\\\\"},JSON.stringify=function(t,e,n){var r;
if(gap="",indent="","number"==typeof n)for(r=0;n>r;r+=1)indent+=" ";else"string"==typeof n&&(indent=n);if(rep=e,
e&&"function"!=typeof e&&("object"!=typeof e||"number"!=typeof e.length))throw new Error("JSON.stringify");return str("",{"":t})}),
"function"!=typeof JSON.parse&&(cx=/[\u0000\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g,
JSON.parse=function(text,reviver){function walk(t,e){var n,r,o=t[e];if(o&&"object"==typeof o)for(n in o)Object.prototype.hasOwnProperty.call(o,n)&&
(r=walk(o,n),void 0!==r?o[n]=r:delete o[n]);return reviver.call(t,e,o)}var j;if(text=String(text),cx.lastIndex=0,cx.test(text)&&
(text=text.replace(cx,function(t){return"\\u"+("0000"+t.charCodeAt(0).toString(16)).slice(-4)})),
/^[\],:{}\s]*$/.test(text.replace(/\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g,"@")
.replace(/"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g,"]")
.replace(/(?:^|:|,)(?:\s*\[)+/g,"")))return j=eval("("+text+")"),"function"==typeof reviver?walk({"":j},""):j;
throw new SyntaxError("JSON.parse")})}();
// * One line to easily feed into BT call.
function hexDecode (str) { var r=''; for (var i = 0; i < str.length; i += 2){ r += unescape('%' + str.substr(i, 2));} return r; }
function hexEncode (str) {
var r = ''; var i = 0; var h; while (i < str.length) { h = str.charCodeAt(i++).toString(16); while (h.length < 2) { h = h; } r += h; } return r;
}
function getTrimmedError (errorMessage) {
var displayMessage = errorMessage;
if (displayMessage.length > 200) {
displayMessage = displayMessage.substring(0, 200);
}
return displayMessage;
}
function styleThePath (pathItem) {
pathItem.stroked = true;
pathItem.strokeWidth = 4;
pathItem.strokeColor = app.activeDocument.swatches.getByName("Black").color;
}
function checkDocument () {
if (app.documents.length == 0) {
throw new Error(BTSample.name + ": No documents are open.");
}
}
function makeACircle (xy) {
checkDocument();
var doc = app.activeDocument;
var x = xy[0]; // * Array argument example.
var y = xy[1];
var circle = doc.pathItems.ellipse(y, x, 60, 60);
styleThePath(circle);
return { circleResult : "A circle was made at [" + x + ", " + y + "]." };
}
function makeASquare (xyObj) {
checkDocument();
var x = xyObj.x;
var y = xyObj.y;
var doc = app.activeDocument;
var square = doc.pathItems.rectangle(y, x, 60, 60);
styleThePath(square);
return { squareResult : "A square was made at [" + x + ", " + y + "]." };
}
function checkMethod (methodName) {
if (!methodName || typeof methodName !== "string") {
throw new Error("Method is invalid");
}
var availableMethods = "|" + [
makeACircle.name,
makeASquare.name
].join("||") + "|";
if (availableMethods.indexOf("|" + methodName + "|") == -1) {
throw new Error("Method '" + methodName + "' is not among the available methods: [" + availableMethods.split("||").replace(/\|/g, "") + "].");
}
return true;
}
// * An object which is a dictionary of methods and their names. A method can be called by using its name and accessing this object.
const METHODS = {
"makeASquare" : makeASquare,
"makeACircle" : makeACircle,
};
/**
* The method which sends this entire script and running instructions via BridgeTalk.
* @Param {string} methodName - Method that will execute out of this script.
* @Param { Record<string, unknown> | any[] | boolean | string | number } arguments - An argument which will be serialized into JSON.
* @Param {(res: { body: string }) => void} [onResult] - Optional method to perform a follow-up action in the ScriptUI window.
*/
function makeBTCall (methodName, arguments, onResult) {
checkMethod(methodName);
var argString = "";
if (typeof arguments == "object" && arguments !== null) {
argString = JSON.stringify(arguments);
} else if (arguments instanceof Array) {
argString = JSON.stringify(arguments);
} else if (typeof arguments == "boolean") {
argString = JSON.stringify({ booleanValue : arguments });
} else if (typeof arguments == "string") {
argString = JSON.stringify({ stringValue : arguments });
} else if (typeof arguments == "number") {
argString = JSON.stringify({ numberValue : arguments });
}
if (argString) {
argString = "JSON.parse(hexDecode('" + hexEncode(argString) + "'))";
}
var callLine = BTSample.name + "('" + methodName + "'" + ((argString ? ", " + argString : "")) + ");";
var scriptString = hexDecode + "; eval(decodeURI(hexDecode('" + hexEncode(
encodeURI(
BTSample.toString() + "\n" +
callLine
)
) + "')));";
var bt = new BridgeTalk();
bt.target = "illustrator";
bt.onError = function (error) {
alert(getTrimmedError(error.body));
}
bt.onResult = onResult || function (result) {
alert(result.body);
}
bt.body = scriptString;
bt.send();
}
function start () {
var w = new Window("palette", BTSample.name);
var g1 = w.add("group");
var btn_1 = g1.add("button", undefined, "Action 1");
var btn_2 = g1.add("button", undefined, "Action 2");
function callMakeACircle (xyArray) {
makeBTCall(makeACircle.name, xyArray, function (res) {
var parsedBody = JSON.parse(res.body);
e_res.text = parsedBody.result.circleResult || parsedBody.result;
});
};
function callMakeASquare (xyObj) {
makeBTCall(makeASquare.name, xyObj, function (res) {
var parsedBody = JSON.parse(res.body);
e_res.text = parsedBody.result.squareResult || parsedBody.result;
});
};
btn_1.onClick = function () { callMakeACircle([0, 50]); }
btn_2.onClick = function () { callMakeASquare({ x : 50, y : 0 }); }
var g2 = w.add("panel", undefined, "Result:");
var e_res = g2.add("edittext", undefined, "");
e_res.characters = 20;
// * Amazingly, adding this to the window allows the methods to be accessed directly, from a separate JSX file.
// * This is not necessary when calling from CEP where the window-creating code is stored.
// * However, it is necessary to create this object and not assign a function in a loop of `METHODS` keys - then it acts weird and only does `makeACircle`.
w.addedCustomMethods = {
callMakeASquare : callMakeASquare,
callMakeACircle : callMakeACircle,
};
w.show();
}
// * When this script is called for any reason, this entry point decides on how it will be executed.
if (arguments.length == 1) {
if (arguments[0] == start.name) {
start();
}
// * Add any other methods that have no arguments to run them.
} else if (arguments.length == 2) {
var inputMethodString = arguments[0];
checkMethod(inputMethodString);
var inputArgObj = arguments[1];
var result;
try {
result = METHODS[inputMethodString](inputArgObj);
} catch (error) {
result = "ERR_:" + getTrimmedError(error.message);
}
return JSON.stringify({ result : result });
}
}
BTSample("start"); /* // * Cut this part out when using from CEP and just use the "start" as the evalScript method name (Ex: `evalScript("start")`). */
^^^ This is the main script.
//@targetengine main
// Window.find("palette", "BTSample").addedCustomMethods.callMakeASquare({ x : 400, y : -300 });
Window.find("palette", "BTSample").addedCustomMethods.callMakeACircle([200, -400]);
^^^ This is a simulation of how an external call would physically work. In fact, you could pass this as a string to the CEP evalScript method. But, all you would need to pass to evalScript is this: evalScript("BTSample('makeACircle', '" + JSON.stringify([120, 350]) + "')") It theoretically should work running from the buttons in the SUI pallette, from a CEP evalScript (in these 2 different ways) and also using the Window.find technique from any JSX script that is ran in Illustrator. Note that once the window is closed, there's no way to show it again unless you restart Illustrator. However, even while the window is closed it can still run the BT methods or regular methods when invoked with the Window.find technique, so externally-triggering the methods still add the shapes to the document. Although, obviously if SUI is doing anything to add text to the edittext input, it's hidden from view. However - even then, it would be possible to use the window reference in Window.find to iterate the window's childen or access other custom properties to gain references to the UI controls and their values or any variables kept running by this window object whether hidden or not. Anyways, my advice is to just avoid BT here since CEP makes the UI easier and since you've gone down the path of CEP then might as well take advantage of all the benefits - unless your BT functions are indeed needed to go between Photoshop or something. In that case still, no need for SUI or any window techniques at all since a normal BT call to another app would still be viable using the regular evalScript CEP method.
... View more