Get path of dragged folder and pass to javascript

Hi All,

I have a javascript that uses photoshop to do some work on TIFF images. What I would like to do is have an applescript that will allow the following.

  1. Drop folder onto script
  2. Script makes sure it is a folder, if not warning dialog
  3. Get the path to the folder
  4. convert the path to be javascript compatible. Not familiar with syntax for paths in javascript. Might be “.” not “:” or “/”
  5. set variable topFolder to the javascript ready path

The javascript runs fine from an application bundle like this


-- Get the path to the application bundle
	set basePath to path to me as text
	-- Create the full path to the javascript in the bundle
	set sScriptPath to (basePath & "Contents:Resources:SaveAsTIFF_LZWoverwrite.js") as alias
	--tell photoshop to run the javascript
	tell application "Adobe Photoshop CS3"
		do javascript "sScriptPath
	end tell

but the javascript puts up a dialog asking the user to select a folder. I want to comment that out in the javascript and instead have the applescript pass a variable to the script. The variable topFolder is already in the javascript so I just need to pass it a value for the folder dragged onto the script.


on run
	display dialog "Drop folders onto this script"
end run

on open draggedItems
-- Get the path to the folder dropped onto script
set topFolder to --(path to the dragged folder)
	-- Get the path to the application bundle
	set basePath to path to me as text
	-- Create the full path to the javascript in the bundle
	set sScriptPath to (basePath & "Contents:Resources:SaveAsTIFF_LZWoverwrite.js") as alias
	--tell photoshop to run the javascript
	tell application "Adobe Photoshop CS3"
		do javascript "sScriptPath(\"" & topFolder & "\");"
	end tell
end open

I realize others can’t fully test this without the javascript but that part is working fine. In a nutshell I need an applescript that will return the path to the folder dropped on it.

Any Ideas?

This script sort of gets what I need.


choose folder
set thisItem to result
tell application "Finder"
	set itemType to get kind of thisItem
end tell
--return itemType
if itemType is "folder" then
	set topFolder to thisItem as alias
	display dialog ("Path " & return & topFolder & return & "Is a folder.")
end if

and this one using draggedItems gets to the section that calls the javascript and then gets an error because the javascript is not there. If I place the javascript in the bundle I can’t drop things on it.


on run
	display dialog "Drop folders onto this script"
end run

on open draggedItems
	repeat with thisItem in draggedItems
		tell application "Finder"
			set itemType to get kind of thisItem
		end tell
		--return itemType
		if itemType is "folder" then
			set topFolder to thisItem as alias
			display dialog ("Path " & topFolder)
		else
			display dialog "Only drop folders on this script"
		end if
	end repeat
	-- Get the path to the application bundle
	set basePath to path to me as text
	-- Create the full path to the javascript in the bundle
	set sScriptPath to (basePath & "Contents:Resources:SaveAsTIFF_LZWoverwrite.js") as alias
	--tell photoshop to run the javascript
	tell application "Adobe Photoshop CS3"
		--do javascript sScriptPath
		do javascript "sScriptPath(\"" & topFolder & "\");"
	end tell
end open

Thanks,
Mark

OK, I’m making some progress I think. I guess having a package open, “Show package Contents” keeps things from being able to be dragged on it so it wasn’t from adding the script to the bundle. I think I just need to resolve the path being in a form javascript will accept. So far I have


on run
	display dialog "Drop folders onto this script"
end run

on open draggedItems
	repeat with thisItem in draggedItems
		tell application "Finder"
			set itemType to get kind of thisItem
		end tell
		if itemType is "folder" then
			set myFolder to thisItem as alias
			set myFolder to POSIX path of myFolder as text
			--display dialog ("Path " & myFolder)
		else
			display dialog "Only drop folders on this script"
		end if
	end repeat
	-- Get the path to the application bundle
	set basePath to path to me as text
	-- Create the full path to the javascript in the bundle
	set sScriptPath to (basePath & "Contents:Resources:Scripts:SaveAsTIFF_LZWoverwrite.js") as alias
	--tell photoshop to run the javascript
	tell application "Adobe Photoshop CS3"
		--do javascript sScriptPath
		do javascript "sScriptPath(\"" & myFolder & "\");"
	end tell
end open

The path to the dragged folder is found but it seems incomplete as it starts with “Users” instead of the volume name. I’ve tried using POSIX style and alias but still get an error. Anyone know how javascript likes file paths?

Thanks,
Mark

Hi Mark,

In the PS CS3 Scripting Guide there is an example

tell application “Adobe Photoshop CS3”
set theFile to alias “Application:Documents:MyFile”
open theFile
do javascript (file ) with arguments { 75,2,89 }
end tell

I don’t know if this works, or the path to the folder must be POSIX or HFS
but the syntax should look like this,
I removed also the unneeded coercions


on run
	display dialog "Drop folders onto this script"
end run

on open draggedItems
	repeat with thisItem in draggedItems
		if folder of (info for thisItem) then
			set myFolder to POSIX path of thisItem
		else
			display dialog "Only drop folders on this script"
		end if
	end repeat
	-- Get the path to the application bundle
	set basePath to path to me as text
	-- Create the full path to the javascript in the bundle
	set sScriptPath to (basePath & "Contents:Resources:Scripts:SaveAsTIFF_LZWoverwrite.js")
	--tell photoshop to run the javascript
	tell application "Adobe Photoshop CS3"
		--do javascript sScriptPath
		do javascript (file sScriptPath) with arguments {myFolder}
	end tell
end open

Thanks StefanK,

Awesome as always. There is still an issue getting the javascript to use the variable being passed. I’m not sure if the the javascript is expecting POSIX or HFS either or if it needs a path including the volume as the current script returns /Users/mark/Desktop/Test/ and I’m not that familiar with javascript. It also seems that I will need to modify the javascript a little to get the path into the function that needs it. Below is the javascript.

/*
This script will scan folders and subfolders for tiffs, open and compress them with LZW compression.
It can be modified to open and recompress jpegs or to open tiffs and save jpegs.

/
var Q = 9; //the jpeg quality used if saving as jpeg
var openType = "
.tif";//change this to open pdf or jpg if you choose
var saveType = “tif”; //will also save pdf or jpg format

function main(openType,Q,saveType){
var topFolder = myFolder //Folder.selectDialog(“Select the top folder containing your images.”)
var docPath = topFolder;
var whatFolders = new Array();
whatFolders = scanSubFolders(topFolder)

var tFileList;
var fileList;
var whatsK = 1;

if(whatFolders.length == 1){whatsK = 0;}
  for(var k = whatsK; k < whatFolders.length; k++){
	var docFolder = new Folder(whatFolders[k]);

	var files = docFolder.getFiles(openType)
	var docRef;

	for(i=0;i<files.length;i++){

		if(files[i].readonly == true){files[i].readonly = false;}
		try{docRef = open(files[i])} catch(e){ }
		//var docRef = activeDocument; //the open document, used later to save
		var docPath = docRef.fullName.parent.fsName;	

		saveType = saveType.toLowerCase();
		if(saveType == "jpg" || saveType == "jpeg") saveAsJPG(docRef,docPath,Q);
		if(saveType == "tif" || saveType == "tiff") saveAsTIFF(docRef,docPath);
		if(saveType == "pdf") saveAsPDF(docRef,docPath,Q);
			
	    docRef.close(SaveOptions.DONOTSAVECHANGES); 	


	}  

  }//end first for loop	

}
function saveAsTIFF(dRef,dPath){
tifFile = new File(dPath + “/” + dRef.name); //theDocumentName

TifSaveOptions = new TiffSaveOptions();
TifSaveOptions.imageCompression = TIFFEncoding.TIFFLZW;
TifSaveOptions.embededColorProfile = true;
TifSaveOptions.layers = false;
	
dRef.saveAs(tifFile, TifSaveOptions, false, Extension.LOWERCASE); //(name,options,as copy?,ext);

}
function saveAsJPG(dRef,dPath,Q){
jpgFile = new File(dPath + “/” + dRef.name);

          jpgSaveOptions = new JPEGSaveOptions();
          jpgSaveOptions.embedColorProfile = true;
          jpgSaveOptions.formatOptions = FormatOptions.STANDARDBASELINE;
          jpgSaveOptions.matte = MatteType.NONE;
          jpgSaveOptions.quality = Q;
      dRef.saveAs(jpgFile, jpgSaveOptions, false, Extension.LOWERCASE); //(name,options,as copy?,ext);

}
function saveAsPDF(dRef,dPath,Q){

      pdfFile = new File(dPath + "/" + dRef.name);
   var pdfSaveOptions = new PDFSaveOptions();
	   pdfSaveOptions.alphaChannels = false;
	   pdfSaveOptions.annotations = false;
	   pdfSaveOptions.downgradeColorProfile = true;
	   pdfSaveOptions.embedColorProfile = true;
   
	   pdfSaveOptions.encoding = PDFEncoding.JPEG;
	   pdfSaveOptions.interpolation = false;
	   pdfSaveOptions.jpegQuality = Q;
	   pdfSaveOptions.layers = false;
	   pdfSaveOptions.spotColors = false;
	   pdfSaveOptions.transparency = false;
	   pdfSaveOptions.useOutlines = false;
	   pdfSaveOptions.vectorData = false;
       	dRef.saveAs(pdfFile, pdfSaveOptions, false, Extension.LOWERCASE); //(name,options,as copy?,ext);

}

function scanSubFolders(tFolder)
{
var sFolders = new Array();
var allFiles = new Array();
sFolders[0] = tFolder;

for (var j = 0; j < sFolders.length; j++) // loop through folders
{
var procFiles = sFolders[j].getFiles();

    for (var i=0;i<procFiles.length;i++) // loop through this folder contents 
    { 
     if (procFiles[i] instanceof Folder){sFolders.push(procFiles[i]); 
} 
  } 

}
return sFolders;
}

main(openType,Q,saveType,myFolder);
// end of javascript

The applescript does trigger the script but then I get an error that myFolder is undefined at the line in the javascript that calls the “main” function

main(openType,Q,saveType,myFolder);

I may need to find a javascript forum to find out how to handle the variable being passed.

Thanks for your help,
Mark

A general JavaScript forum probably would not help. You need to know the specifics of how PS CS3 passes the arguments supplied to the with arguments . AppleScript parameter to the JavaScript code. I asked Google for photoshop cs “do javascript” AppleScript “with arguments” and it found Adobe Photoshop CS3 Scripting Guide that seems to have the info you need (italics comments are mine):

I also searched through the Adobe’s Introduction to Scripting, but I could not find anything about passing arguments from AppleScript to JavaScript.

Anyway, it looks like that last line in your script should be this:

main(openType,Q,saveType,arguments[0]);

Also, you need to update your definition of the main function so that it takes four arguments (make topFolder the fourth argument and delete the assignment to it):

function main(openType,Q,saveType,topFolder){ var docPath = topFolder; â‹® }
As for Mac-style vs POSIX-style pathnames, the reference I found was Chapter 3 of Adobe’s JavaScript Tools Guide. It says that you can specify paths in either a platfrom-specific way or their platform-independent way (URI). I did not find the parts that covered pathname interpretation to be all that perspicuous, but it seems that maybe it will try to deal with anything you hand to it.

Note: I don’t have PS CS3 (or any other version of PS), so I cannot test this.

Thanks chrys,

I found that same thing in the PS CS3 Scripting Guide and modified my script. I get an error in the function scanSubFolders on line 114.

var procFiles = sFolders[j].getFiles();

The error is

ERROR 24: sFolders[0].getFiles is not a function.

It appears that the path is getting passed but it may not be what the script was expecting. The javascript originally called up a dialog to select a folder and that was set to topFolder.

Thanks for your help,
Mark

Mark,

This is no longer really AppleScript related, but I will try to see you through this, since I think you are pretty close to getting it working.

The error suggests to me that the topFolder variable should hold a Folder object, not just a pathname string. Furthermore, documentation for Folder.selectDialog() (in the JavaScript Tools Guide) says that it will return a File or Folder object as appropriate. Try changing the call to main() (at the bottom of your posted JavaScript file) to be this:

main(openType,Q,saveType,new Folder(arguments[0]));

Hi chrys,

That did it! So when passing a folder or file reference to a javascript either POSIX path or HFS should work but in the javascript the folder or file object has to be declared using the provided path that was passed in the arguments.

That’s why I was considering a javascript forum but thanks for continuing help.

Back to Applescript:
So I added a sub folder to the folder I’ve been testing with and it is causing the script to pass the path of the sub folder and not the path of the actual folder that was dragged onto the script. Thinking the script would grab the path of the folder dragged onto it first I added a exit repeat, but it seems to find the sub folder first. What’s the easiest way to get the path to the folder dragged onto the script regardless of what is in the folder being dragged. The javascript will scan the directories for pre-defined file types.


on open draggedItems
	--Make sure a folder was dropped and get the path to it
	repeat with thisItem in draggedItems
		if folder of (info for thisItem) then
			set myFolder to POSIX path of thisItem
			exit repeat
		else
			display dialog "Only drop folders on this script"
		end if
	end repeat
	
	-- Get the path to the application bundle
	set basePath to path to me as text
	
	-- Create the full path to the javascript in the bundle
	set sScriptPath to (basePath & "Contents:Resources:Scripts:SaveAsTIFF_LZWoverwrite.js")
	
	--tell photoshop to run the javascript and send the argument myFolder
	tell application "Adobe Photoshop CS3"
		activate
		do javascript (file sScriptPath) with arguments {myFolder}
	end tell
end open

Thanks,
Mark

After writing and rewriting this post, writing test AppleScript code, repeatedly reading your post #9, and finally noticing a bad assumption I had been making, I think I have found your problem. It is in the JavaScript code though.

The problem centers around the whatsK variable in the JavaScript code. This bit of code seemed strange to me from my reading but I just tried to ignore the feeling. At first glance I assumed that scanSubFolders() just created an array containing the immediate subfolders of its argument. But when I paid better attention to the code, I saw that it does a breadth-first search of its argument folder to produce an array containing that initial folder and all its sub^n-folders (similar to {myFolder} & folders of entire contents of myFolder in AppleScript of Finder).

If the folder you pass to main() (topFolder) does not have any subfolders, then the result of scanSubFolders() is an array with just one item: topFolder (for the pedants, the same value as the value stored in the variable topFolder, not the variable itself). This result is stored in whatFolders. whatsK, a new variable, defaults to 1, but since the length of whatFolders is 1 (there were no subfolders), whatsK is reset to 0. Then whatsK is used as the starting index when looping through whatFolders. JavaScript arrays are 0 based, so the first element is at index 0. topFolder is always at index 0. So, the file contents of topFolder are processed.

If topFolder does not have any subfolders, it unfolds differently. whatsK defaults to 1. scanSubFolders() returns an array that starts with topFolder and is followed by all of its sub^n-folders. The length of whatFolders is not 1, so whatsK is not reset. The loop through whatFolders starts at index 1 (the value of whatsK is still 1). Index 1 means the second item. This means that topFolder (always at index 0) is skipped.

I do not know why the JavaScript was written this way. But it sure looks like the original author intended to not process the file contents of topFolder iff it has any subfolders. If you want to process the file contents of topFolder, even if it has subfolders, change the k loop to start at 0, and then delete the conditional reset of whatsK (if(whatFolders.length == 1){whatsK = 0;}) and the declaration and initialization of whatsK (var whatsK = 1;).

Back to AppleScript

If you only drop one folder on the script, then the myFolder variable will end up holding the POSIX path of the single folder you dropped. This is true for both Stefan’s version in post #4, and your version with exit repeat in post #9. The two versions of the script will, however, put different paths in myFolder if more than one folder is dropped onto the script. If you want to handle that case, the AppleScript will have to be changed a bit (collect a list of POSIX path of folders, then tell PS CS3 to run the JavaScript once for each path (or modify the JavaScript to take a list of paths (if lists of strings can be properly passed))).

Since I already wrote this up as a part of research for this post, I will include it. Here is a version that does several things differently:
¢ Avoids causing the system to calculate the total size of items when using info for (this can be very slow when a folder contains lots of items either directly or in sub^n-folders).
¢ Processes all folders dropped on the script.
¢ Issues only one “warning” if non-folders are dropped on the script.
¢ Issues a notice if no folders are dropped on the script.
¢ Optionally displays debug information for the items dropped on the script, and result of filtering the list for folders. The debug code is pretty ugly, but I am leaving it in as an example of how one could have tested your original script to see exactly what is being passed to the JavaScript code.
¢ Optionally drops the trailing slash from strings passed to PS CS3 JavaScript. (While casting about for what might cause your problem, I hypothesized that the trailing slash was perturbing the JavaScript code. It seemed very unlikely, but it was easy to implement, so I wrote it up.)
¢ Coerces path to me to Unicode text instead of text for better compatibility. (In AppleScript 2.0 (Mac OS X 10.5), text is the same as Unicode text, so it does not matter. In previous releases, some paths are not representable as text, so Unicode text is usually better (unless your AppleScript is so old that Unicode text support is spotty).)
¢ References to PS CS3 are commented out, since I do not have it. Delete the occurrences of “–HAVE PS CS3–” to enable the PS CS3 code.

to displayDebugList(aPrompt, aList)
	try
		if class of aList is not list then set aList to {aList}
		set displayList to {}
		repeat with i in aList
			set i to contents of i
			set str to "<unknown>"
			if (class of i is text) or (class of i is string) or (class of i is Unicode text) then
				set str to i
			else if class of i is alias then
				set str to "alias: " & POSIX path of i
			else if (class of i) is «class cfol» then
				set str to "Finder folder: " & POSIX path of (i as alias)
			else
				set str to "<unknown>"
				try
					set str to ((class of i as Unicode text) & " as utxt: ") & i
				end try
			end if
			set end of displayList to str
		end repeat
		if displayList is {} then set displayList to {"<Empty Debug List>"}
		choose from list displayList default items {} with title "Debug Info" with prompt aPrompt with empty selection allowed without multiple selections allowed
	on error m
		display dialog "An error occured while generating debug info: " & m with title "Error"
	end try
end displayDebugList

to getFolderItemsPOSIXPaths(draggedItems)
	set foundNonFolder to false
	set folderItems to {}
	repeat with thisItem in draggedItems
		if folder of (info for thisItem without size) then
			set end of folderItems to POSIX path of thisItem
		else
			set foundNonFolder to true
		end if
	end repeat
	
	if (count folderItems) is 0 then
		display dialog "No folders found in dropped items. Drop one or more folders on this app for processing." buttons {"OK"}
	else
		if foundNonFolder then
			display dialog "Only the folders dropped on this app will be processed. Non-folders will be ignored."
			if button returned of result is "Cancel" then
				error "User cancelled."
			end if
		end if
	end if
	
	folderItems
end getFolderItemsPOSIXPaths

(*
Delete all occurrences of "--HAVE PS CS3--" to enable PS CS3 statements.
*)

property showDebugInfo : true
property dropTrailingSlashes : false

on open draggedItems
	if showDebugInfo then displayDebugList("Initial List", draggedItems)
	
	--Make sure a folder was dropped and get the path to it
	set myFolders to getFolderItemsPOSIXPaths(draggedItems)
	
	if showDebugInfo then displayDebugList("POSIX Paths of Folders", myFolders)
	
	-- Get the path to the application bundle
	set basePath to path to me as Unicode text
	
	-- Create the full path to the javascript in the bundle
	set sScriptPath to (basePath & "Contents:Resources:Scripts:SaveAsTIFF_LZWoverwrite.js")
	
	--HAVE PS CS3--tell application "Adobe Photoshop CS3" to activate
	repeat with myFolder in myFolders
		set myFolder to contents of myFolder
		if dropTrailingSlashes and ¬
			length of myFolder is greater than 1 and ¬
			myFolder ends with "/" then ¬
			set myFolder to text 1 thru -2 of myFolder
		true --HAVE PS CS3--and false
		if result then display dialog "Processing " & myFolder
		--tell photoshop to run the javascript and send the argument myFolder
		--HAVE PS CS3--tell application "Adobe Photoshop CS3" to do javascript (file sScriptPath) with arguments {myFolder}
		
	end repeat
end open

Hi Chris,

Your solution works great! Sorry for the delay but our power has been out due to ice.

Thanks so much for your help,
Mark