Most Mac Hackers are well aware of AppleScript. What most are not aware of is that AppleScript is merely one implementation of Apple's Mac OS Open Scripting Architecture, or OSA. Mac OS comes pre-installed with AppleScript, but there is at least one other OSA language available: JavaScript.
Late Night Software, a company that makes AppleScript tools, has ported the Mozilla JavaScript 1.5 interpreter as an OSA component. Installing it will allow you to use JavaScript on Mac OS wherever you could use AppleScript. Here are a few reasons you might want to do this:
JavaScript has several features that AppleScript lacks, such as associative arrays and regular expressions.
JavaScript is available on other operating systems while AppleScript is not, so you can share JavaScript code and expertise between different platforms.
There is considerably more documentation and sample code available for JavaScript than AppleScript.
|
To run JavaScript from Mac OS, you must first download the OSA component from Late Night Software's JavaScript OSA page. The web page includes instructions for installing the JavaScript OSA on Mac OS 8, 9, and X. After you have installed it, launch the Apple Script Editor application and choose "JavaScript" from the language menu in the bottom left of the Script Editor window.
|
Now you're ready to write your first JavaScript. Type the following text into a Script Editor application window and click "Run":
Core.message("Hello World!");
If an alert box with the words "Hello World!" appear, it worked!
If you get an error, make sure you have "JavaScript" selected in the scripting language pop-up menu and that you typed the command in exactly as specified. JavaScript is case-sensitive, so "Core" and "core" are different names and only "Core" will work.
Don't worry if a result window appears with the word "undefined" in it. Script Editor automatically displays the value returned from scripts, and this sample script does not return any results.
|
If you've used JavaScript in web browsers before, you may be wondering where "Core" comes from and why window.alert("Hello World!") won't work. Neither "Core" nor "window" are part of the JavaScript language -- they are objects supplied by the scripted application to JavaScript.
|
Related Reading
|
Most web browsers, including Netscape Navigator and Microsoft Internet Explorer, use JavaScript internally and do not supply their internal objects to OSA components. The window.alert method is internal to the web browser and not available to OSA components.
The JavaScript OSA component provides some basic functionality in the Core and Mac OS objects for all applications. The methods and properties available in these objects are documented at the Late Night Software JavaScript OSA pages. However, the most useful objects for scripting are supplied by applications and not the JavaScript component.
You can find what scripting objects an application supplies by opening the application's scripting dictionary from Apple's Script Editor application. Scripting dictionaries contain AppleScript terminology, and there are some significant differences in JavaScript terminology:
JavaScript does not allow spaces in names. The JavaScript OSA component
converts spaces in application dictionaries to underscore characters (_), so
you would type the name "startup disk" in the Finder dictionary as
startup_disk.
The JavaScript OSA component uses arrays where AppleScript uses lists. You can create array literals in JavaScript by enclosing the items in bracket characters and separating them with commas. For example, the AppleScript list {1,2} is represented as [1,2] in JavaScript. Note that in AppleScript the first item of a list is item 1, while in JavaScript the first item has array index 0.
AppleScript has the notion of a "result" object which contains the last value or object used. JavaScript does not have such an object, if you want to use a calculated value in another statement, you must assign it to a variable.
AppleScript allows referring to inherited objects directly while
JavaScript requires specifying the entire hierarchy to the object. For
example, in AppleScript you can refer to the startup disk of the desktop
object directly in Finder, while in JavaScript, you must use
desktop.startup_disk since the startup disk is a property of the desktop
object.
With this in mind, we can start to write JavaScript for scriptable Mac OS applications. Finder is a good application to experiment with since it does a lot of useful things. Remember the Wild Hard Drive Mac Hack that had a simple AppleScript to move the startup disk icon? The (slightly modified) AppleScript version is:
tell application "Finder"
activate
select startup disk
set position of selection to {630, 79}
end tell
We can do the same thing in JavaScript:
var finder = MacOS.finder();
finder.activate();
finder.select(finder.desktop.startup_disk);
finder.desktop.startup_disk.position = [630, 79];
|
It's not that useful to convert working AppleScript to JavaScript, but it is useful to convert working JavaScript on another platform to run on Mac OS. Chapter 1 of O'Reilly's book, JavaScript: The Definitive Guide, includes the following JavaScript written for a web browser:
document.write("<h2>Table of Factorials</h2>");
for(i = 1, fact = 1; i < 10; i++, fact *= i) {
document.write(i + "! = " + fact);
document.write("<br>");
}
The Mac OS does not have a built-in document object, so we will use the JavaScript OSA's Core object. The converted script is:
var s = "Table of Factorials\n";
for(i = 1, fact = 1; i < 10; i++, fact *= i) {
s += "\n" + i + "! = " + fact;
}
Core.message(s);
Running this script from Script Editor gave the same results as running the original script in Netscape Navigator, Internet Explorer, or iCab.
You can save JavaScripts as standalone applets with Script Editor; just save the script as a "classic applet" for Mac OS 8 or 9, or as a Mac OS X applet for Mac OS X. Anyone can run applets; you don't need Script Editor or need to know JavaScript.
If you want to use the same JavaScript on different platforms, it's useful to create wrapper objects that work the same on each platform. Rather than changing all occurrences of window.alert to Core.message in your browser scripts, you could define an object literal named window with a property named alert that contains a function literal which calls Core.message in the JavaScript OSA with the same argument passed to the property.
var window = {alert: function(s) {Core.message(s)} };
If you haven't used literals before this may look confusing. All it does is allow you to call:
window.alert("Hello World!");
in your JavaScript code, and have the Mac OS JavaScript OSA call Core.message. The same principle can be used to override any application objects in your existing JavaScript code.
|
Related Reading
|
If you're interested in exploring the JavaScript OSA further, take a look at some of the files in the Sample Scripts folder that comes with JavaScript for OSA. This folder contains sample application scripts, web CGIs, applets, and examples of using the Core and Mac OS objects.
So if you can do everything in JavaScript that you could in AppleScript, why would you ever want to use AppleScript? Unfortunately, JavaScript has a few drawbacks for scripting Mac OS applications:
JavaScript often takes more memory than AppleScript. This most often manifests itself as an "out of memory" or "error -108" message while running a script. To run the script, you will have to quit the application, increase its Preferred Size in the Memory Requirements section of the application's Info window in Finder, and run the script again. I increased the Preferred Memory of the Script Editor application to 3,000 K. You will also need to increase the preferred size of any JavaScript applets you create.
JavaScript is not recordable. You will not be able to create JavaScript from an application by clicking the "Record" button in the Script Editor.
There are a few bugs in using application objects from JavaScript. You may have noticed in the Wild Hard Drive example, I converted an AppleScript statement that set the position of a selection, to a JavaScript statement that set the position of the startup disk. Why didn't I use the JavaScript object finder.selection[0]? It seems there is a bug in the JavaScript OSA component in returning properties of the startup disk from a selection. Properties of other files, even other disks, will work, but the property of the startup disk does not. You may run into similar problems with other application objects.
JavaScript is an interesting language that has enormous potential on Mac OS. Aside from providing an alternate method of scripting existing applications, it provides a way to create your own cross-platform Macintosh applications.
Richard Hough is a web developer for a Vancouver, Canada educational software company.
Return to the JavaScript and CSS DevCenter.
Copyright © 2009 O'Reilly Media, Inc.