Skip to main content
Known Participant
September 21, 2005
Question

Opening a Script in PS from Bridge

  • September 21, 2005
  • 11 replies
  • 1191 views
The following script was (severely) hacked from Bob's OpenClose_PS.jsx. It is called from Bridge and runs the target script in Photoshop. It seems to work even with complex scripts that go from Photoshop back to Bridge to do more things in Bridge.

#target bridge

function getScript ( ) {

var scp = "remoteScript = " + remoteScript + "\n";

scp += "remoteScript();";

return scp;

}



function remoteScript ( ) {

var f = new File ('/c/Program Files/Adobe/Adobe Photoshop CS2/Presets/Scripts/ah-rawprocessor1.js');

if (f.open('r')) {

var sStr = f.read();

}

else throw('failed to open script'+f.fsName);

eval(sStr);

}



function runScript ( ) {

var bt = new BridgeTalk ();

var theScript = getScript();

bt.target = "photoshop";

bt.body = theScript;

bt.send();

}



runScript();


BUT I do not understand what is going on in the first line of the getScript function - or perhaps in the whole of that function. I would like to understand it better and as part of that find a way to specify the target script path within the runscript() call ie runScript(targetScript);

Andrew
This topic has been closed for replies.

11 replies

Known Participant
September 23, 2005
Hmmm, that's a new one for me. When would the Function constructor be useful?

I see how what you are describing is different than functions used as constructors for objects which is where I confused this thread.

--John
Known Participant
September 23, 2005
Function constructors let you define classes and do object oriented programming in JavaScript. I've been doing it quite a bit because, since my programming history is C++, it's the way the programming part of my brain thinks. It's pretty cool.

There is lots written about this on the web if you search for "Javascript classes methods". Here's one summary of it that also describes how you can create public, private and privileged methods for your class: http://phrogz.net/JS/Classes/OOPinJS.html.

--John
Known Participant
September 23, 2005
jfriend00@adobeforums.com wrote:
> Function constructors let you define classes and do object oriented programming in JavaScript. I've been doing it quite a bit because, since my programming history is C++, it's the way the programming part of my brain thinks. It's pretty cool.
>
> There is lots written about this on the web if you search for "Javascript classes methods". Here's one summary of it that also describes how you can create public, private and privileged methods for your class: .
>
> --John

Function constructors are a different beast from object/class constructors.
Function constructors allow you to programmatically construct function 'objects'
like this:

trim = new Function("str",
"return str.replace( /^[\s]+|[\s]+$/g, '');");

It may not seem particularly useful here, but it can let you write extremely
concise code if the problem can be expressed suitably.

ciao,
-X
Known Participant
September 22, 2005
Anyone on a Mac - what is the default install URI of Program Files/Adobe/Photoshop CS2/

Andrew
Known Participant
September 22, 2005
John, thanks so much for that, you were pretty much right about the Menu thing too. I'm still finding my way around the Bridge Scripting reference, and I missed it though I did look for it.

Andrew

A few minutes later - ahh, that is useful but it doesn't solve the specific problem as none of those Folder references provides a cross platform way of getting to the Photoshop folder. Fortunately Bob's method does.

I think I might create a run-once-only script that sticks a dat file somewhere that remembers the Photoshop address for bridge.
Known Participant
September 22, 2005
In Bridge, look at the Folder object. It has the following class properties:

Folder.appData
Folder.commonFiles
Folder.current
Folder.myDocuments
Folder.startup

FYI, this is on page 138 of the Bridge Scripting Reference.

--John
Known Participant
September 22, 2005
Also, a warning - the new AppLauncher beastie hasn't been tested. It's a work in progress - use as your own risk!

Bob
Adobe WAS Scripting
Known Participant
September 22, 2005
Just to make sure - you're creating a script (in Bridge) from a PS script in PS' presets/scripts folder, that will eventually be sent to PS - correct?

Send 2 BT messages.

The first one gets the script path. Put the result of that into your script before you actually send it to PS.

You'll get the result in the onResult handler.

var bt = new BridgeTalk();
bt.target = "photoshop";
bt.body = "app.path;"
bt.onResult = continueOp; // function that continues the process

continueOp = function( msg ) {
var path = msg.body;
// do the rest
}

You have to be careful - there's a bug in BT that causes a queued message (waiting for an app to launch) to un-bind from it's onResult handler. The result is that if you launch PS with a BT message that expects an onResult handler to fire, it won't. Once PS is launched and ready, this isn't a problem.

There's a function - startTargetApplication( target );

that will start the target app and wait until it's ready. You can use that, but it locks bridge up until the target app is fully launched.

Another way is to use this new beastie I've been working on:

AppLauncher = {};
AppLauncher.array = new Array();
AppLauncher.processID = null;
AppLauncher.running = false;
AppLauncher.pause = false;
AppLauncher.id = 0;
AppLauncher.launchTimeout = 2 * 60 * 1000; // 2 minutes
AppLauncher.launchCycle = 3000; // launch and check launch status every 3 seconds
AppLauncher.purge = function() {
AppLauncher.array = new Array();
try {
app.cancelTask( AppLauncher.processId );
} catch ( e ) {
}
AppLauncher.running = false;
AppLauncher.pause = false;
}
AppLauncher.launch = function( target, callback, context ) {
if ( !BridgeTalk.isRunning( target ) ) {
var launcher = new AppLauncher.Launcher( target, callback, context );
AppLauncher.array.push( launcher );
if ( !AppLauncher.running ) {
AppLauncher.running = true;
AppLauncher.processId = app.scheduleTask( "AppLauncher._$launch();", AppLauncher.launchCycle, true );
}
} else {
if ( context ) {
callback.call( context, true, target );
} else {
callback( true, target );
}
}
}
AppLauncher._$launch = function() {
// $.writeln( "launching" );
if ( !AppLauncher.pause ) {
for ( var i = 0; i < AppLauncher.array.length; i++ ) {
AppLauncher.array[ i ].launch();
}
}
if ( AppLauncher.array.length == 0 ) {
if ( AppLauncher.processId != null ) {
app.cancelTask( AppLauncher.processId );
AppLauncher.running = false;
AppLauncher.pause = false;
}
}
}
AppLauncher.Launcher = function( target, callback, context ) {
this.id = AppLauncher.id++;
this.target = target;
this.callback = callback;
this.context = context;
this.calledback = false;
this.launched = false;
this.t1 = new Date();
}
AppLauncher.Launcher.prototype.timedOut = function() {
return ( ( new Date() - this.t1 ) > AppLauncher.launchTimeout );
}
AppLauncher.Launcher.prototype.resultHandler = function() {
if ( this.context ) {
this.callback.call( this.context, true, this.target );
} else {
this.callback( true, this.target );
}
}
AppLauncher.Launcher.prototype.kill = function() {
AppLauncher.pause = true;
var array = new Array();
for ( var i = 0; i < AppLauncher.array.length; i++ ) {
if ( AppLauncher.array[ i ].id != this.id ) {
array.push( AppLauncher.array[ i ] );
}
}
AppLauncher.array = array;
AppLauncher.pause = false;
}
AppLauncher.Launcher.prototype.launch = function() {
if ( this.timedOut() ) {
this.kill();
if ( this.context ) {
this.callback.call( this.context, false, this.target );
} else {
this.callback( false, this.target );
}
} else {
if ( !this.launched ) {
BridgeTalk.launch( this.target );
this.launched = true;
}
if ( BridgeTalk.isRunning( this.target ) && ( !this.calledback ) ) {
this.calledback = true;
if ( this.context ) {
this.callback.call( this.context, true, this.target );
} else {
this.callback( true, this.target );
}
this.kill();
}
}
}

The usage is:

AppLauncher.launch( target, onLaunchedHandler, context );

target is the target app
onLaunchedHandler is a callback to execute when the app has been lauched.

context - is a context object - used ONLY if your onLaunchedHandler is a method of an instance of an object. IF THAT IS THE CASE - include the object as the context. If it is NOT the case, leave as undefined.

onLaunchedHandler = function( success, target ) {
}

success - boolean -true if app launched and responded
target - target app that launched.

Example:

var ns = {};
ns.continue = function( success, target ) {
// make and execute script
}
AppLauncher.launch( "photoshop", ns.continue );

fred = fuction( target, script ) {
this.target = target;
this.script = script;
}
fred.prototype.continue = function( success, target ) {
// make and execute script
var bt = new BridgeTalk();
bt.target = this.target;
bt.body = this.message;
bt.send();
}
fred.prototype.send = function() {
AppLauncher.launch( this.target, this.continue, this ); // note the context object here because the onLaunchHandler is attached to an instance object

}

var f = new fred( "photoshop", "app.path;" );
f.send();

This on will not freeze bridge while waiting for launch.

Bob
Adobe WAS Scripting
Known Participant
September 22, 2005
Sorry to be so pesky - another question. I want to specify the target script to Bridge in a platform independent way - and the target script will be in the Photoshop/Presets/Scripts folder. Within PS I can use app.path which starts me off at Photoshop, but I just noticed that Bridge does not seem to have such an app property. Is there a way round this.

Andrew
Known Participant
September 22, 2005
I agree that it is a lot nicer than in-line. The bit I don't get is the:

+ remoteScript +

What is this remoteScript variable and what does it insert.

I guess I am not used to this constructor / variable way of defining and referring to functions - is it the equivalent of remoteScript().toSource().

I was going to ask you about this before, in my JS reference (1.5) it writes:

"Function objects created with the Function constructor are evaluated each time they are used. This is less efficient than declaring a function and calling it within your code, because declared functions are compiled."

As an aside, I ended up wrapping my skeleton script above in a string, and then using string.replace() to change the target script (for multiple menu items) BUT it seems something in ES does not like the "\n" (or '\n') so I cut it out. It is a possible bug in the scripts as string approach - you can see what I mean in my template script:

http://www.ps-scripts.com/bb/viewtopic.php?t=186

(with the '\n' removed it works, with it included it does nothing).

Thanks again for your help

Andrew
Known Participant
September 23, 2005
Andrew-Hall@adobeforums.com wrote:
> I agree that it is a lot nicer than in-line. The bit I don't get is the:
>
> + remoteScript +
>
> What is this remoteScript variable and what does it insert.

remoteScript evaluates as a string in this context. You would get something like
'function() {
// contents of remoteScript
}'

>
> I guess I am not used to this constructor / variable way of defining and referring to functions - is it the equivalent of remoteScript().toSource().
>

It's actually a little more complicated than that. You can define functions
three different ways:

1) function statements/declarations
function sq(x) { return x * x; }
2) Function constructors
var sq = new Function("x", "return x*x;");
3) function literals
var sq = function(x) { return x * x; }

The scoping and closure rules are slightly different for each. Function literals
and statements are compiled once, Function constructors are compiled each time.

I haven't found a pressing need to use Function constructors yet. My code is
about as obscure as I want it to get, for the moment.

ciao,
-X
Known Participant
September 22, 2005
Look at the context of the string "scp" and think about it in the context of what you're sending to PS.

When both lines are done, the value of scp should be:

remoteScript = function() {
//whatever the contents of remote script were
}
remoteScript();

Which in PS, defines a function and then executes it.

When we started, we were just "inlining" the generated scripts for execution in the target apps - this case is a PS script

var scp = "var inFile = eval( " + f.toSource() + " ); \n" +
"var outFile = eval( " + outputFile.toSource() + " ); \n" +
"var startDialogMode = app.displayDialogs; \n" +
"app.displayDialogs = DialogModes.NO; \n" +
"var doc = app.open( inFile ); \n" +
"doc.saveAs( outFile, opt, true, Extension.LOWERCASE ); \n" +
"doc.close( SaveOptions.DONOTSAVECHANGES ); \n" +
"app.displayDialogs = startDialogMode; \n";

If you want to see something really hard, look at ContactSheet_ID.jsx.

The way we're doing it now:

remoteScript = function() {
// some really cool scripty stuff
}

var scp = "remoteScript = " + remoteScript + "\n";
scp += "remoteScript();";

Is VASTLY easier than attempting to generate a script with everything inline.

Bob
Adobe WAS Scripting