JavaScript for OS X Automation by Example

Apple is very close to releasing a new operating system called Yosemite. Everyone has been talking about iOS 8, iPhone 6, iPhone 6 Plus and watches, but one important feature was JavaScript for OS X Automation. While it may have been overlooked by journalists, it was not overlooked by Telerik. Burke Holland created an excellent post that described how JavaScript is now a first-class citizen. In this post, I am going to show you how you can use JavaScript for OS X Automation, since there is hardly any information on it besides the preliminary documentation on Apple’s site.

Note: I am using APIs still in development. Anything shown below is subject to change.

Getting Started

After you have installed Yosemite, then go to “Applications” -> “Utilities” and drill down until you see the Script Editor as shown in Figure 1

Figure 1 : Script Editor.

Figure 1 : Script Editor.

Previously, your only option for creating automated tasks was AppleScript, now you will see JavaScript has been added.


Select JavaScript and let’s build something.

Example: Automating an Email Message

If you have automated tasks in the past, then surely you have interacted with the Mail app. Below is a snippet written in AppleScript.

tell application "Mail"
    set theMessage to make new outgoing message with properties 
     {visible:true, subject:"Thanks for buying from us!", content:"My Body"}   
end tell

I’ve taken the same snippet and appended slightly more complex content, but wrote it entire using JavaScript.

Mail = Application('Mail');

content = "Hi Michael,\n\n"
      + "Hello, How are you! \n\nThanks for stopping by today!"
      + " We really appreciate your business \n\n"
      + "Sincerely,\n\n"
      + "Company Name";

msg = Mail.OutgoingMessage({
    subject: "Thanks for buying from us!",
    content: content,
    visible: true



A couple of things to note here:

  • Semicolons are optional because of Automatic Semicolon Insertion (ASI).
  • I instantiated a variable in the global scope; instead of using for example :
    var Mail = Application(‘Mail);
  • I can use either single or double quotes for hard coded values.

If this script is run, then the following email dialog will appear as shown in Figure 2

Figure 2 : Email Message with Fields populated.

Figure 2 : Email Message with Fields populated.

But How Do you Know What Properties are avaiable?

Good question, if you are inside of Script Editor then you can click on “Window” -> “Library” to see a list of available apps that you can interact with. If we pick the Mail app then it will default to AppleScript, simply change that to JavaScript and you can see the properties that are available on the OutgoingMessage class that we just used in Figure 3.

Figure 3 : Examining the OutgoingMessage class from the Documentation.

Figure 3 : Examining the OutgoingMessage class from the Documentation.

That’s Cool, but What About Other Applications?

The Notes app is another application that we can interact with, but Apple has put in security precautions to prevent malicious scripts. We can interact with the Notes app with just a few lines of JavaScript.

Notes = Application('Notes');

SystemEvents = Application('System Events');
Notes = SystemEvents.processes['Notes'];[0].splitterGroups[0].groups[1].groups[0].buttons[0].click();

If you run this application, then you will be greeted with the following dialog shown in Figure 5.

Figure 5 : Security & Privacy Dialog.

Figure 5 : Security & Privacy Dialog.

If you click on “Open System Preferences”, then you can grant this script access and the Notes app will open automatically.

I’m sure by now you are wondering how to run these scripts without being inside the Script Editor application. You can do so by clicking on “File” -> “Export” then change the format to “Application” as shown in Figure 6.

Figure 6 : From a script to an app!

Figure 6 : From a script to an app!

Other Automation Tasks

You may not want to interact with an application, maybe you wish for your app to speak to the user. You can accomplish this with just a few lines of JavaScript

App = Application.currentApplication();
App.includeStandardAdditions = true;
App.say("Hello from Telerik Headquarters");

This will use the default voice and language installed on your system to speak the words, “Hello from Telerik Headquarters”.

You can also use it to capture input such as a Name for example:

App = Application.currentApplication();

App.includeStandardAdditions = true;

answer = App.displayDialog('Please enter your Name', {
  withTitle: 'Name',
  defaultAnswer: 'Telerik'

This will display a prompt (Figure 7) and you can see in the Results window what they actually typed (Figure 8). In this case, I typed my name, “Michael”.

Figure 7 : Dialog Message from JavaScript Automation

Figure 7 : Dialog Message from JavaScript Automation

Figure 8 : Results Window

Figure 8 : Results Window

JavaScript Automation can also be Accessed through the Terminal

Surprisingly enough, JavaScript Automation can also be helpful for those that use bash to automate tasks. Here is an example command line that opens Safari and a new tab and navigates to by using osascript interactive mode. The rest is plain JavaScript that we talked about before.

osascript -l JavaScript -i
Safari = Application("Safari");
window =[0];;
tab = Safari.Tab({url:""});
window.currentTab = tab;

The below screenshot shows what it looks like in the terminal window in Figure 9

Safari will open and generate a new tab as shown below:


Look back through the terminal window and notice that when I called it returned “Top Sites”. This could be useful for understanding what page a user is on.

There’s an Objective-C Bridge to Advantage Of

This is useful if you wish to use frameworks not present in the Foundation framework used by default. You can take advantage of frameworks like Cocoa by implementing the following code.

str = $.NSString.alloc.initWithUTF8String('Writing text to file through obj-c bridge.');
str.writeToFileAtomically('/Users/MichaelCrump/FromObjCBridge.txt', true);

This code imports the Cocoa Framework, initializes an NSString with text and calls the writeToFileAtomically method passing in the file location and setting the second pararameter to true. This will ensure that the old file will not be changed or removed until the new version is completely on the disk.

We can navigate to our folder and open the text file and see the expected results in Figure 10

Figure 10 : Obj-C Bridge Writing a File to Disk

Figure 10 : Obj-C Bridge Writing a File to Disk

Even more Opportunities for JavaScript Developers

We are learning more and more about the implications of JavaScript becoming a first-class citizen in Yosemite. It is also front and center for NativeScript. NativeScript enables developers to use JavaScript to build native apps leveraging readily available or custom device APIs, such as those for camera, accelerometer, geolocation and more. There is a lot to talk about with NativeScript, but I’ll point you to the FAQ if you want to learn more.

NativeScript allows you to build native applications for iOS, Android and Windows Universal using JavaScript.

Bright Future Ahead

I’m very excited about what I’ve seen so far with JavaScript for OS X Automation. Prior to writing this article, I’d never used AppleScript before, but had written JavaScript. I was able to use the syntax familiar to me and successfully write automation tasks. JavaScript developers all over the world should rejoice even if they don’t plan on writing automation tasks because it shows that skillset is becoming more and more important.

Header image courtesy of Dhilung Kirat


  • Pingback: Dew Drop – September 22, 2014 (#1860) | Morning Dew()

  • Carlos Rodrigues

    I did not have to instantiate a variable; for example :
    var Mail = Application(‘Mail);

    You did, but in the global scope.

  • aaronj1335

    thanks for the post, especially the part about osascript’s interactive mode! good to know there’s a repl for this stuff 🙂

    i was messing around with this the other day and found some interesting stuff while trying to write a script that positions windows:

    thanks again!

  • Really nice article, thanks for the tips.

  • Karim Maassen

    Looking forward to it. Yosemite is upping the ante!

  • Pingback: WinJS 3.0 Shows Industry Shift Towards JavaScript -Telerik Developer Network()

  • mklement0

    Great intro, thanks. The REPL is handy, though I couldn’t figure out how to *preload functionality* into it; the man page says that code specified *in addition* to `-i` is *loaded, but not executed*. However, such preloaded code then doesn’t seem to be available in the REPL, e.g.:
    osascript -l JavaScript -i -e ‘function tst() { return “hi” }’
    >>tst() // FAILS with: ‘… ReferenceError: Can’t find variable: tst’

    Am I missing something?

  • Len Swierski

    Great article, but your email example does not show how to fill in the ToRecipient field and then send the message. When I try to do this, I get: Error -1700: Can’t convert types. An Applescript I use does this easily, so I assume that my syntax is wrong. Can you show the syntax for creating a ToRecipient and then how to send the message? Thanks!

    • mklement0

      After the `msg =` statement, add the following `msg.make(); msg.toRecipients.push(Mail.Recipient({name:”Foo Bar”, address:””}))`. This adds a “to” recipient with the specified display name and email address. Note the somewhat obscure requirement to call `.make()` on the message (so as to actually create it) before you can access its `.toRecipients` element.

      As for sending the message: `msg.send()` SHOULD work, but apparently doesn’t as of OSX 10.10: mistakenly tries to send a new, blank email form instead. I encourage you to file a bug at

      • Len Swierski

        Thank you so much, mklement0. That did the trick. Using ‘Mail.send(msg)’ worked for sending. I read somewhere that empty parenthesis could cause a problem. Maybe that’s why ‘msg.send()’ does not work.

        • mklement0

          My pleasure, Len; thanks for the feedback on sending. Unfortunately, `Mail.send(msg)` misbehaves in the same way as `msg.send()` on two of my machines – not sure what’s going on.

          • Len Swierski

            Although my script now works, it leaves a dock icon open. Can’t figure out how to close it from the script. To be more precise, I saved the script as an application, and the dock icon left open is the same as any other script that runs (Script Editor icon with application name) but that usually goes away when run as an Applescript. Hmph.

        • Len Swierski

          I got it working perfectly. The trick was to uncheck the “Stay open after Run Handler” checkbox when saving as an application. There seems to be no “Save As” in Script Editor, so copying the script to the clipboard and pasting into a new document was the answer.

          • mklement0

            Glad to hear it. The “Save As” command does exist, but is somewhat hidden: Hold down Option while opening the File menu to see it (alternatively, use ⌥⇧⌘S) – this should work on all native Mac apps.

      • mklement0

        Turns out there was no bug: I mistakenly called *both* `msg.make()` *and* `Mail.outgoingMessages.push(msg)` to create the object, which caused the problem. Use either one *or* the other.

  • Pingback: 5 cool Yosemite tips for teachers - Innovation: Education()

  • Harry Moreno

    I see you run `osascript …` Can you provide a sample bash script that calls osascript in this fashion. Or better yet, a node script or npm module that can run javascript-for-applescript.

  • Mikefulton

    Is there a way to do simple console output while developing your script? That and the lack of debugging options are what I’m tripping over.

  • Dru_Satori

    Has anyone sorted out how to do the equivalent of an AppleScript ‘as’ statement ? for example, you have a Finder File reference and you want the POSIX Path, In AppleScript you would simply ‘set myPath to POSIX Path of (file reference as alias)’ and you are good to go. I cannot figure out how to get the same thing from JavaScript for Automation 🙁

    • JMichaelTX

      Here’s some sample code that may provide some insight into getting paths in JXA:




      How To Get Path to Temp Folder (JXA)


      DATE: November 20, 2015

      AUTHOR: Rob Trew (@ComplexPoint)


      • Learning & Using AppleScript & JavaScript for Automation (JXA) – general – Keyboard Maestro Discourse


      // — AppleScript Equivalent —

      // path to temporary items from user domain

      var a = Application.currentApplication(),

      sa = (a.includeStandardAdditions = true && a),

      lstOptions = [

      // –> Path object

      sa.pathTo(‘temporary items’, {

      from: ‘user domain’


      // –> Unix string

      sa.pathTo(‘temporary items’, {

      from: ‘user domain’


      // –> HFS string

      sa.pathTo(‘temporary items’, {

      from: ‘user domain’,

      as: ‘string’





      I used the MD code for code blocks, but it doesn’t seem to work. 🙁