Transcript
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
>> Welcome.
Welcome to Friday.
Thanks for being here.
My name is Sal Soghoian.
I am the Product Manager
for Automation Technologies
at Apple, Inc.
And this is Session
306, this is a big day.
We get to welcome
JavaScript to the family
of scripting languages on OS X.
[ Applause ]
We've been waiting
a long time for this
and I know our customers
have, too,
and we're really
thrilled about it.
And before I get into the
details of the language
and how it works I'd like
to take a moment to reflect
on the state of automation,
beginning with Mavericks.
Now, in Mavericks we introduced
a host of new features,
some very powerful
things beginning
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
with notification support in
both the scripting language
and in Automator as well.
So it became very easy to
use notifications instead
of posting dialogues in
your automation routines.
We also introduced
Code Signing ability
in our Editor applications,
both in Automator
and in the Script
Editor, which is great
because then you could sign and
deliver Automation solutions
to your customers,
friends and coworkers.
We also introduced
a new construct
in AppleScript called the
"use" statement that acts
like an importer to import
functionality from applications
and frameworks and scripting
editions, and allow them
to be used in your scripts.
And by doing this we
also made it possible
to have Script Libraries
that we introduced.
And Script Libraries are
scripts that you create yourself
that contain your favorite
sub-routines or handlers,
and you can call and load these
libraries from scripts anywhere
in the operating system.
You can also take advantage of
AppleScript Objective-C and any
of the Cocoa calls
that you want to use.
So they were very powerful,
very useful they be introduced.
And then finally, we had
a new feature that worked
on English-only systems but we
thought it was so interesting
that we introduced it.
It was called Speakable
Workflows, and it allowed you
to take your Automator workflow
and save it as a speakable item
in the dictation architecture.
So you could say a command and
it would run your workflow.
And so Mavericks was a very
productive release for us,
we were very excited about
it, but we were also excited
about what we've
done in Yosemite
and how it builds upon the
groundwork we laid in Mavericks.
As part of this evolutionary
process,
we integrated Code Signing
with workflow files.
So not only applets but now
your workflow files can be
Code-Signed as well.
And because of changes
that we implemented
in the AppleScript language for
supporting optional parameters
in sub-routines and
handlers we were enabled
to upgrade the scripting
library support
so that you can now have your
favorite calls have optional
parameters and use
those as well.
Very good improvement there.
And listening to customer
requests that have been
for years and years,
requesting some way
to indicate a progress method
for their Automation routines,
we now have built-in
script progress indicators.
So, by calling simple properties
you can have your scripts
display a progress
indicator, a circle,
or even as a progress
bar in a floating window,
and all of this is done for
you automatically, all you have
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
to do is set properties
within your scripts.
So I think our customers
are going to really enjoy
that particular feature.
And we took the idea
of Speakable Workflows
and we've advanced it now
because of the dictation
architecture in Yosemite
that works with all languages.
We now have a new
template item in Automator
for creating a Dictation
Command.
So you create your workflow,
you save it, it gets saved
into the dictation architecture
and when you're in the process
of dictating or just
sitting at the machine,
you can say the name of your
command and it will execute.
Very interesting, very
powerful, I know you're going
to have a lot of
fun exploring that.
So that's just a quick
overview of where we've been,
but something that's been
on our minds for a long time
that we've happy
and very pleased
to have here today is
JavaScript for Automation.
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
Now, those of you that are
familiar with JavaScript
from the web-based community,
this is not the JavaScript
for writing a DOM that uses CSS
and HTML5 to create a web app.
We're talking about extending
the JavaScript core language
to integrate with the open
scripting architecture.
And by doing this, you
have now gained the ability
to pull all those levers
inside of the scriptable apps
on your operating system to
perform the kind of repetitive
or interesting or complex
tasks that you do every day.
And now this power is available
to you through JavaScript.
Now many of you that write
JavaScript professionally
or as your focus, you're used
to using editors like BBEdit,
Sublime Text, WebStorm,
TextMate, things like that.
Initially, as you adapt to
JavaScript for Automation,
you'll write your first scripts
in the Script Editor
application.
And I'm going to give
you just a short overview
of this application, its
interface and preferences
so that you'll understand
what's happening
as David presents
JavaScript for you.
So let me take a look at the
Script Editor application.
So when you open the Script
Editor Application you get this
very simple interface,
this is a script window.
And it has a simple but
very powerful design to it,
beginning with the little slider
that separates the two
main panes of the window,
the script pane where you
enter the code for your script,
and the bottom pane
contains an event log.
So as the script is executing
you'll see the events listed
there with the results
coming in from the events.
And this is a live
log that happens
as the script is executing.
At the top there are
some simple controls
for basically compiling
the script
to make sure your
syntax is correct,
and there's also a control for
running or stopping your script.
Now, there at the bottom
here is a language control.
So make sure if you're
writing JavaScript
that this popup menu is set
for the JavaScript language.
And that's basically
the interface,
and the way that it works is
you enter your script code
into the top area, then
you click the Run button
and as it executes you can see
that there's a progress
indicator here
on the right showing the
progress of the script,
and you can watch
the event log go by.
Now this simple script that
exported open Keynote documents
to movies, you can
see the results of it
and how it was logged
and everything indicated
in the final result of
the script at the bottom.
So this is the interface
that you have
when you're executing scripts,
and as David shows
you the examples,
this is what you're
going to see.
There's some other important
preferences you should be aware
of and let me just
quickly show those to you.
So, in the Preferences menu
from the Application menu,
in the General Settings
pane there is a Default
Language popup.
If you're going to
be using JavaScript
as your primary language for
scripting you want to make sure
that you change this
value to JavaScript.
Now this says JavaScript 1.0.
This is JavaScript
for Automation 1.0.
It is not JavaScript 1.0.
We use the current JavaScript
core language as the basis.
So don't be freaked out
when you see 1.0 there.
Also, make sure that you
check the Show Script menu
in menu bar option, and what
this will do is activate the
system-wide Script Menu at the
top right of your menu bar.
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
And from this Script Menu you
can host your favorite scripts
and you can make them
available contextually
within certain applications.
So if you write scripts for the
Finder, they can only appear
if the Finder's there.
If you write scripts for Pages,
they'll only appear
when Pages are there.
It's up to you, the Script
Menu runs shell scripts,
Apple scripts, applications,
workflows, JavaScripts,
any kind of scripting
language supported
by the operating system can
run from the Script Menu.
So you'll be taking
advantage of that.
In addition, if you want to set
certain coloring for the code
that you use, this formatting
pane is where you'll do that.
You can choose fonts, font
size and coloring to apply
to fit comfortably with
what you're used to.
Next, I'd like to
look at dictionaries.
Now, in the operating system,
every scriptable
application carries internally
within its bundle a
complete dictionary
of all the terms
that it understands.
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
It will list all the classes and
all the methods and the commands
and the properties and elements
that it is familiar with.
So this is where you will
go to learn about how
to control a particular
application.
And what you do is you
choose Open Dictionary
from the File menu and in the
forthcoming dialogue choose
an application.
When you do that you'll
see a script viewer window
like this one for the
Keynote dictionary,
and this is what you
see when you look
at an application's
scripting dictionary,
and this is what you will do
to navigate that dictionary.
At the top is a Language button,
so this dictionary
viewer functions
for all the scripting
languages we support,
so it supports JavaScript,
AppleScript or Objective-C,
and you can change the mode
for viewing the content
right from this menu.
Now at the top part of
this window, the top pane
of this window, is
the Model Viewer.
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
And this is where you
see how the objects
in the application
scripting model are reflected.
On the left-hand side, typically
a scripting dictionary is
grouped into suites, and these
suites are usually grouped
by functionality,
by what they do.
In this particular
example you'll see
that there's multiple suites.
There's suites for iWork.
Because Keynote is part of the
iWork family of applications,
the iWork Suites
of common objects
and common commands
are included there.
And then Keynote has
its own suite of things
that are particular for
the Keynote application.
And then once the
suite is selected,
you'll see in the next column
the methods and the classes
that belong to that
particular suite.
So here we have commands
like Export,
where you would export
a presentation.
And you see classes
like document.
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
And then when you select a
class, you'll see the elements
that belong to that class
in the right-hand column,
and also the properties
of that particular class.
And here you see the properties
of a Keynote document.
Now for further information
and detail about these,
in the bottom half of the
window is the dictionary viewer.
And the dictionary viewer
contains the definitions for all
of those particular elements.
So when you want to
know about, well,
how do I script the Finder,
or how do I script Numbers,
how do I script Keynote,
how do I script Aperture?
You open a dictionary and this
is where you learn and explore
to find out how to
control those applications.
So that's the scripting
dictionary,
and that's the Script
Editor application.
So, let me get out of the way
and bring up David Steinberg,
who's the newest
member of our team,
to show you JavaScript
for Automation.
Enjoy this [applause].
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
>> Thank you, Sal.
Hello, everyone.
Thank you for being here.
It's a great honor.
I'm really excited to be showing
you this awesome new feature
of OS X Yosemite,
JavaScript for Automation.
So today's talk will cover what
JavaScript for Automation is
and how it works, how you can
use JavaScript for Automation
from basic to more
advanced techniques,
and finally we'll talk
about where you can use
JavaScript for Automation.
So to talk about what
JavaScript for Automation is,
let's take a look at
the underlying pieces.
Automation allows you
to accomplish tasks
on your Mac automatically,
from simple tasks,
such as changing the name of
a hundred files all at once,
to more complex tasks, such as
sending personalized directions
to guests before a
party you're having.
I'd like to demonstrate the
power of Automation to you now.
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
So I work for Acme
Widgets, we're a company
that makes widgets,
as you might expect.
We have three main
clients, Japan, USA,
and then across Europe, and here
I have a chart detailing some
of our widgets' prices and
the regions they go to,
and the particular
currency, and so on.
And now I have four
upcoming presentations.
First, I have to give a
presentation to the president
of the company about all of
the regions that we work with,
all of their individual
prices, currencies, and so on,
and then I have to give
presentations to each
of those regions only detailing
the particular information
that pertains to that region.
So, now I'm thinking, okay,
I have four Keynotes
I have to make.
Well, let's start
with the first.
I have all my data here,
it's formatted as I want it,
I have some images that I
can use related to this data.
How about I write a script
that does this for me?
Well, I've done just that.
So here I have a script, don't
worry about understanding all
of it at the moment, but it's a
JavaScript for Automation script
that uses this Numbers document
and produces a full slide
deck for me in Keynote.
So if I run this here, we see
our slides are being created
for us with the information
that we got from that data,
and now we have an entire slide
deck ready for my president
to see, detailing
all of our widgets,
their prices per region,
currency, and so on.
This is perfect.
Okay, so I've accomplished
one, completely automatically.
I have three more to do,
and each of those are going
to be pretty much
the exact same.
Same number of slides, same
images, same information,
changed per region with
particular currency and so on.
And in fact, all of that
information is already
in this presentation I have
here, I just need to slim it
down for each version.
So I wrote myself
a helper script,
I'm going to use
it multiple times,
so I put it in my script menu.
And what this does
is it lets me say,
where am I presenting today?
Well, today I'm going to
be presenting in Japan.
So I just slim down my
Keynote to the few slides
that are important, put it
in presentation mode
and I'm able to go.
Hello everyone, thank
you for being here.
Let's talk about prices.
It's been a real
pleasure, have a good day.
Now [laughter], right?
Now, I'm going to be giving
a presentation for the USA.
Okay, I don't want to make
another slide deck all together,
I'm going to use my nice
little helper here, USA.
Hello everybody, thank
you for being here.
Let's talk some dollars.
It's been a real
pleasure, have a nice day.
Beautiful.
That entire automatable
process was automated for me
with just a couple little
scripts that I wrote.
That made my life easier,
got my work done faster.
And that is the power
of Automation.
[ Applause ]
Those demos were made possible
by scripting applications,
and applications that are built
using Cocoa automatically have a
certain level of scriptability,
such as being able
to be told to open, or to print.
And a wide variety
of applications have fully
embraced scriptability,
detailing the Object
Model of the application
and offering rich suites of
functionality beyond the basics.
Apple events are the underlying
communication mechanism behind
application scripting.
They provide access to the
scriptable pieces and parts
of an application
that are defined
in an application's scripting
dictionary, as Sal showed,
detailing what objects can
be interacted with and how.
So when you script
an application
in whatever language you may,
its scripting dictionary is read
and then Apple events are used
to communicate with
the application.
So let's take a look at an
example of an Object Model.
Here we have Mail, which
has an application object
with an inbox property, with
a messages element array,
with each element
individually accessible.
So for example, we could
pinpoint the second message
of the inbox of the application
mail, as an object specifier,
or a reference to
that particular object
in that particular application.
This is great, we've
looked really closely
at the things behind Automation
and application scripting.
Now let's talk about
JavaScript and how the power
of Automation was brought to it.
JavaScript is a scripting
language with C-like syntax
and objects with
prototype-based inheritance.
It's most well known
in the browser being the
scripting language of HTML5.
But it's also become
quite popular
as a server-side scripting
language, evidenced by Node.js.
JavaScript on OS X is
powered by JavaScriptCore,
the engine used by
WebKit and Safari.
JavaScript for Automation
is built on JavaScriptCore.
It's using the modern JavaScript
environment, and it's a kind
of server-side JavaScript,
or as we like to call it
OS Automation JavaScript.
And while the underlying
engine is the same,
JavaScript for Automation
is quite different
from running scripts
in your browser.
When you use JavaScript
for Automation,
you can do everything that
you as the Mac user can do.
And this is very different
from browser scripts
that don't have access
to your personal data
or the applications on your Mac.
And in browser scripts you
might expect a window object
or a document object, but you're
not going to have those here.
You have a host of
different objects
that you can interact
with, to automate your Mac.
So how exactly was this
brought to JavaScript?
Well to recap, applications
have scripting dictionaries
that detail what can be
interacted with and how,
and Apple events are
used to communicate
with those applications.
So to bring this
to JavaScriptCore we built the
JavaScript Apple Event Bridge
that reads those
scripting dictionaries
and then uses Apple
events to communicate
with the applications.
So now that we've seen what
JavaScript for Automation is
and how it works, let's look
at how we can use JavaScript
for Automation, beginning
with scripting applications.
We introduced an
application object
into a JavaScript context,
which is the main entrance point
to all of the scriptable pieces
and parts of an application.
So, the most common way
to access an application
is by name.
Here we're accessing
the Safari application.
And beyond name we can
also access applications
by Bundle ID, by
Path, by Process ID,
and we can get the application
that's running the script we're
currently in by getting
the current application.
So now that we have
this application object,
we're going to want
to interact with it.
Let's look at some examples of
the syntax for doing just that.
Accessing properties is
accomplished using dot notation.
To access elements, you
use square brackets.
To call commands, you call them
as functions using parentheses.
And you can create new objects
by calling class constructors
as functions, again,
using parentheses.
So let's take a look at
getting and setting properties.
Let's say we're scripting
Safari, we create a reference
to the first document of
Safari, and now we can do things
like get the document's URL.
See the parentheses at the end?
We're calling this property
as a function because we want
to actually send the get
event to the application
and get the string value.
We don't simply want
to reference to the URL
of the document, we want
to call it as a function
and get that string value.
We can also set the
document's URL,
and this sends the set
event to the application
and actually sets the URL.
Let's say we want to get a
particular window in Safari.
Well, there are three ways to
access elements in JavaScript
for Automation: by
index, by name and by ID.
Each has benefits and
risks associated with it
and it's important you
understand the Object Model
of the application that
you're scripting to know
when it's best to use which.
So already with just these
basics we can do some pretty
amazing things.
Let's put it to use.
First, I'd like to just
open up Script Editor.
We've got a little JavaScript
here, just straight JavaScript.
Creating a string, some
text, and then I'm going
to use console.log,
just like I expect
in JavaScript, to log this text.
And when I log this,
it's going to show
up in the event log
on a lower panel.
So I'm going to open
the event log,
and if I run this script we see
"hello world" was logged
just like we wanted.
This is great.
This is the JavaScript
I'm used to.
It's doing exactly
what we wanted.
This is hugely useful
for debugging,
we can log throughout a
script to see what's going on.
But let's say I wanted to
write some text and I wanted it
to exist even after I quit
Script Editor, or perhaps I want
to style that text, change
its font or color and so on.
Now we can start interacting
with applications
to accomplish that.
So here we have a TextEdit
document named Log,
and I'm going to use this
across a couple demos
to write some text.
First, this is a simple script,
we access the TextEdit
application, we get a reference
to the Log document, then
we set the document's text
and we style its
font, size and color.
So before I run this I'm
going to open the Event Log
and we'll be able to see the
events that are being sent
to this application
to accomplish this.
So when I run this script,
we see "I love JavaScript" was
logged just like we wanted,
styled just liked we wanted, and
in the bottom here we see all
of the events that were sent
to accomplish this task.
So this is great, we have
some persistent text,
it's been beautified
just like we wanted.
But what if we want to
do this multiple times
from the same script?
Well, instead of repeating the
functionality again and again,
we're playing in JavaScript,
we can write a function.
And I've done just that.
Here we have a function that
accepts some text to log
and an optional formatting
parameter.
Then, just like before, we
access the TextEdit application,
we get a reference to the
log document, but now instead
of setting the document's entire
text, we're going to append
to the last paragraph.
Then we're going to style
based on formatting you pass in
or some default values.
Now that we have our log,
we call it three times
at the bottom here, passing in
parameters for formatting twice,
and the last time we
use default values.
So if I run this script, we see
that our text was logged just
like we wanted, styled
as we wanted.
And this is great, we're able
to log as many times as we want,
style any different way that
we choose, every single time.
So this is powerful stuff simply
by accessing an application,
an element, and a
couple properties,
right out of the box,
power at your fingertips,
and it's really easy
to approach.
Let's look at some other
ways that we might want
to interact with applications.
Let's say we're scripting Mail
and we want to access some
of the messages in our inbox,
but we don't want the entire
messages element array
of the inbox, that's a whole
lot of messages usually.
Let's say we want to get only
the messages whose subject
is JavaScript.
Using the special "whose" method
of element arrays
we can do just that.
By passing in a dictionary of
properties we'd like to match
on those elements we now receive
an array only containing the
elements that match.
Objects have certain commands
that they can respond to
and you can call those
commands as methods locally
that send the command
events to the application.
So again, let's say
we're scripting Mail,
we have a reference to the
first message of our inbox,
and we want to open it.
Well, we could tell
the message to open,
we could also tell Mail
to open the message.
And both of these are
entirely equivalent.
Some commands take
named parameters,
and so you accomplish this
by passing in a dictionary
of those named parameters.
Here, for example, we're
creating a reply to our message,
we're going to reply
to everybody
and we're not going
to open a window.
Now remember, you can always
use the dictionary viewer
in Script Editor to see
which named parameters go
with which commands, which
commands can be called
on which objects, and so on.
It's a phenomenal
tool and really useful
when you're building
more complex scripts.
Some commands take
files as parameters,
such as opening a
document using TextEdit.
So to accomplish this we've
introduced a path object
into the environment that
you construct with a string
that has a string with a file
path inside and now you can use
that path object anywhere that a
file is expected as a parameter.
So we could open our
document just like we wanted.
Be aware that if you
pass a string instead
of a path wherever
a file's expected,
you will receive an error.
So let's say we want to
create a document instead
of simply opening
one in TextEdit.
Well, we're scripting TextEdit,
we can call the document class
constructor as a function.
But now to actually create this
object inside the application,
bring it to life, we're
going to push the document
onto the documents
element array.
And that actually creates
it inside the application.
Then we can interact
with it just
like any other objects
inside the application.
We can set its text,
for example.
And optionally, when you
create objects, you can pass
in a dictionary of properties
you'd like to have set
on that object when
it's created.
Here we're creating a
document, passing in some text
when we instantiate it.
We push again on the
appropriate array,
and it's created
inside the application,
living and breathing, ready
for us to interact with.
So this is a wide
range of functionality,
inherently available in
applications, property access,
element access, calling
commands, creating objects.
But there are script
plug-ins, plug-ins for scripts
that allow you to extend the
functionality of an application.
And the OS has a set of
standard scripting additions
that it ships.
These are available from
every scripting environment,
including JavaScript
for Automation.
So let's say that we're
scripting the current
application and we
want to be able
to use these standard additions.
To do so we're going to set the
includeStandardAdditions flag
to true, and then we can do
things like tell the application
to beep, which is hugely
useful for debugging,
tell the application
to speak some text,
which can extend
our functionality
to a wider audience.
We can also do things like
display alerts and dialogues,
which is great because now
we have user interaction
incorporated with
scripting applications.
So we've covered a
lot of different ways
that we can actually
script applications,
their Object Models, and so on.
Let's look at other uses of
JavaScript for Automation.
Remember earlier when we racked
the logging functionality
in a function so we could use it
multiple times from one script?
Well what if we wanted to log
from multiple scripts,
not just one?
This is the perfect
use of a library.
I've taken that log
function and I've saved it
in a script named Toolbox in
the Script Library's folder.
And now I can access this
library and its functions
from any other script
that I write.
To accomplish this I instantiate
a library object by name,
and file extension is optional,
and then I can those functions
as methods on that
library object.
So this code would log
just like we did before.
And the beautiful thing about
Libraries is they can be written
in JavaScript and Apple Script,
and used in JavaScript
for Automation.
So now let's take a look at how
to create applets using
JavaScript for Automation.
Applets are applications
saved by Script Editor,
and when you run an applet
the script that's saved inside
is run.
Applets have a certain
number of events
that you can create
handlers for,
such as when the applet is
run, when the applet is told
to open documents
or print documents.
There's a special idle
handler that allows you
to perform periodic tasks.
And you can also create handlers
for when an applet is reopened,
or when an applet is quit.
And just like Libraries, you can
have any other functions inside
your applet and those
can be called.
So let's take a look at an
example of a basic applet
that uses a Script Library.
Here we have our log document
like before, and let's look
at the script inside
of this applet here.
At the top we instantiate
our Toolbox Library,
then in our run handler
we log Run in green.
In our idle handler,
we log Idle in orange.
And in quit handler,
we log Quit in red.
So if I double click this
applet to run it we see that Run
and Idle were logged just
like we wanted, and if I go
and quit the applet, we see
that Quit was also
logged, which is great.
This is exactly what we wanted.
And this is a very basic example
of using a couple handlers
and an applet and
using a Library.
But imagine the possibilities
here.
This is double-clickable
JavaScript sitting
on my desktop, ready to interact
with all the scriptable
applications,
Script Libraries and more.
Speaking of more, we've looked
at scripting the Object Model
of an application
extensively, but what if we want
to script the user
interface of an application?
This is accomplished
by using accessibility,
which allows applications
on your Mac to interact
with your Mac on your behalf.
System Events uses
the accessibility APIs
to expose the user
interface of an application.
And in JavaScript
for Automation,
you use the System Events
application to access the pieces
and parts of a user interface.
So let's look at an example.
First, we're going to access
the System Events application.
Then we're going to
access the Notes process
of the System Events processes
elements array, and we're going
to set it to a variable,
notesUI.
Now we can do things
like close a window,
by clicking the first
button of the first window
of Notes user interface.
Let's say we want to send some
keystrokes to this application.
Well to do so, we're going
to access the Notes application
itself and activate it,
bringing it to the front,
and then we could call the
SystemEvents.keystroke command,
passing in 'm', using
'command down'
to simulate a minimize event.
To use accessibility
it must be enabled
in the Privacy preferences,
for applications
like Script Editor,
and so on, to use it.
And the first time you try
to use it, accessibility,
you will be prompted
to enable it.
Okay, so we've looked
at another way
to script an application,
its user interface.
We've looked at scripting Object
Models, Libraries, and Applets.
Now let's look at a more
advanced use of JavaScript
for Automation using system
APIs, which provide access
to really powerful and important
things like the file system
and Cocoa app development
in JavaScript.
We introduced two objects
into the environment
that are your means for
interacting with system APIs.
ObjC and $.
And for those coming from
a browser environment,
$ may mean something
very special to you,
but in our environment
it's a very different $.
ObjC can be used to do things
like bridging JavaScript values
into Objective-C, bridging
Objective-C objects
into JavaScript, and
importing frameworks
that you'd like to use.
So once you've imported a
framework you can access its
classes and functions and
so on using the $ object,
and you can also call $ as
a function, as a shorthand
for bridging JavaScript
values into Objective-C.
So now let's talk about
calling methods on objects.
Well, to begin let's look
at some Objective-C code.
It's quite simple, we're
going to create an NSString
and then we're going to
write that string to a file.
This is the equivalent
JavaScript
for Automation code
to accomplish this.
And there are a couple key
things to focus on here.
First, the Foundation
Framework is available to you
out of the box when you're
using JavaScript for Automation.
So we don't need to
import Foundation.
Then, we use the $ object to
access the NSString class,
we call alloc, and because
it takes no parameters,
we don't need parentheses.
We do not use parentheses here.
Then, we call
initWithUTF8String,
and we're going to be passing
in a parameter, so we call it
as a function and
pass in our parameter.
Now, the method to write to a
file takes multiple parameters,
so we're going to
convert the selector
to a JavaScript method
name, that can catenating
and camelCasing the pieces
together, and then we call it
as a function, passing in the
parameters in the right order.
So now that we've seen
how to write to a file,
let's actually see it in use.
This is a little script.
At the top we create an
NSString with some text we'd
like to write to a file.
Then we create an
NSString with a file path,
and we call this
stringByExpandingTildeInPath
method on it to get
the full path.
And then we write the
string to that file.
And when we write this,
it's going to appear
in this folder here.
So if I run this script,
our file was created,
our text was written
just like we wanted.
This is phenomenal.
And this is a simple example,
but you can imagine the power
and utility that the system
APIs bring to the JavaScript
for Automation environment.
Let's look at other ways that we
can interact with these API's.
Let's say we create
a new NSTask object.
Well, to access its properties
we're going to use dot notation.
So we can access the running
property, get its Boolean value
and react accordingly.
We could also set its
launchPath property, like so.
This will actually
set it on the object.
An interesting thing
of a JavaScript
for Automation bridge,
is the bridged nil.
So, nil in Objective-C
is somewhat similar
to the undefined in JavaScript,
but they're not exactly
the same,
and they differ in key ways.
In Objective-C it's absolutely
valid to call a method
on a nil object, and you'll
receive another nil object.
But in JavaScript, if you call
a method on an undefined object,
you will receive an error.
Therefore, we do not
bridge nil undefined,
we keep it as a bridged
nil object
that you can interact with.
So to create a bridged nil
object, we call the $ function
without any parameters.
Why might you want to
create a bridged nil object,
you're asking?
Well, this allows you
to accomplish things
like pass-by-reference.
So here we're creating a new
NSXMLDocument, and we're going
to pass our bridged nil
object, our error variable,
as the third and
final parameter.
And what'll happen
behind the scenes is
if an NSError object is created,
the bridged nil object
will be replaced by it,
and now our error
variable will point
at that and we can use that.
So once we've created our
document, we can check
if it's nil by calling
the isNil method on it,
and if it is nil we could,
for example, log the user info
from the now populated
error object.
Another exciting thing that
you can do using system APIs is
subclassing objects.
So to accomplish this, you call
the registerSubclass method
on the ObjC object, you pass
in a name of your Subclass,
its superclass, although this
is optional, and will default
to NSObject, any protocols
that your class made here, too.
Properties are defined
as an object
where the keys are the
names of the property
and the values are the
type of the property.
And methods are, again,
an object where the
keys are the selector
and the value is an
object detailing the types
of the method and
its implementation.
Now if you're defining a
function or a method, excuse me,
that's declared in a
protocol or on a superclass,
the types are optional, but for
demonstration purposes we'll
show them here.
So now let's put this to use.
Let's see using a subclass
and creating something
using system APIs.
So here I have an applet, and
this is the script inside,
and this produces a
Cocoa application,
a temperature converter, nice
little guy for us to use.
And there are two
things I'd like to point
out before we actually run this.
You'll see at the top
that we import Cocoa.
Now this allows us to access
things like NSWindow, and so on.
We create our windows
and our inputs,
and then we register
our subclass,
named TemperatureConverter,
that has two methods,
to convert from Celsius
to Fahrenheit,
and from Fahrenheit to Celsius.
And you can see that all of the
logic inside of these methods,
all of the math is
in JavaScript.
Then we create a new
TemperatureConverter object,
hook it up to our inputs, and
bring the window to the front.
So let's run this.
Close this here, and if I double
click this Applet to run it,
we have a nice little Cocoa
application written entirely
in JavaScript.
[ Applause ]
Of course, it does
what we promise.
We can convert our
Fahrenheit to Celsius,
our Celsius to Fahrenheit
and so on.
And this is a little example.
Look at this.
We can create a Cocoa
application in JavaScript,
imagine what you
can do from here.
With all this at
your fingertips,
there is power ready to be used.
And definitely make sure
to check out the JavaScript
for Automation for
release notes,
where you can see other great
things you can do using system
APIs, like binding C
functions into the environment,
another mechanism for
pass-by-reference,
and also passing functions
where blocks are expected.
All right, so we've done
a beautiful whirlwind tour
of all the ways we can use
JavaScript for Automation,
from application scripting,
libraries and applets,
UI scripting, using system APIs.
I'm sure each and every
one of you is just itching
to get your hands on this.
I completely understand.
But before you do let's talk
about where you can use
JavaScript for Automation.
So as all the demos have
shown, you can use JavaScript
for Automation in Script Editor.
You can save Applets and
Droplets from Script Editor,
as we saw, and run them.
As Sal mentioned, and you
saw in the presentation demo,
you can save scripts in the
system-wide Script Menu,
and this is phenomenal for
scripts that you're going
to use repeatedly,
again and again.
Automator, which allows
you to produce workflows,
automating applications
and processes on your Macs,
now has a run JavaScript
Automator action
that lets you incorporate
JavaScript for Automation
into your workflows,
and once you've done
so you can save those workflows
as services that can be used
from any application
and across your system.
And finally, you can use
JavaScript for Automation
in Terminal using the
osascript command line tool.
And this is great, you
can incorporate JavaScript
for Automation into
a Bash script.
And for example, display
an alert when you're
about to finish a
long-running background process.
So we've seen some of the places
where you can use JavaScript
for Automation already,
now let's look at the rest.
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
Let me set the scene, I
work as a photographer
in a travel magazine and I
take pictures for our cover.
And every week I have to
send those cover photos
to the same person
to be approved before I
can submit them the print.
Every week I sent the same
email message to the same person
and all I'm changing
are the pictures.
So, I whipped up a little
droplet here using the
openDocuments handler that
accesses the mail application,
creates a new mail message
for us using some
pre-canned content.
It then uses the Contact
application to get my recipient
of the email message, gets their
name and their email address,
puts it on the message, and
then for each of the pictures
that we've dropped on this
droplet we're going to add them
as attachments to the message,
activate mail and have it ready
to send whenever I'd like.
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
So, here are some of
the photos I took.
I happen to know the last
three are the most beautiful,
so I'm going to drop those
right here onto this droplet.
Our mail message is
created, the content's there,
our recipient's there and all
the pictures we wanted are ready
for us to go.
My workflow was cut from
100% down to 5, 10, 0%.
This made my life easier.
And while I have
this script open,
I'd like to mention
another problem that I have.
I read a lot of JavaScript for
Automation, as you might expect,
and I regularly have
Script Editor windows open,
multiples all at once.
Sometimes I'm running a
Library that I'm going to use
in another script
and I want to be able
to see everything
at the same time.
And so before I hopped on the
Automation train, I might've had
to drag these windows around
the screen, resize them,
make sure corners aren't
overlapping each other,
and so on and so forth.
But then I realized,
why don't I automate it?
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
So I whipped up this script here
that accesses the Script
Editor application,
it gets all of the windows, and
then using the number of windows
and the size of the screen
it carefully, meticulously,
programmatically
tiles the windows
around the screen,
based on their size.
So if I run this from the
script menu, because I use it
so frequently, we see all
of my windows were tiled
exactly as I wanted.
I can see everything
all at the same time.
This is beautiful for
developing multiple scripts
at the same time.
So I used JavaScript
for Automation
to make writing JavaScript
for Automation even easier.
It's powerful, it's
flexible and it's everywhere.
[ Applause ]
Now, speaking of flexibility,
and power, and everywhere,
in fact, we've proven to
ourselves how easy it is
to extend popular Text
Editors for JavaScript,
like Sublime Text, and
TextMate, to extend
and to execute JavaScript
for Automation code
that we're writing in
those applications.
And to prove it to you how
easy it is to write JavaScript
for Automation anywhere
and run it, we've whipped
up this automated workflow
that uses the new Run
JavaScript action,
and what we do is we
access the content
of the front-most document,
we evaluate that as a script,
then we display the result
and we optionally copy it
to the clipboard if we'd like.
I've saved this as a service
and I've attached that service
to a hotkey, so now my work is
very, very slim and easy to do.
I would like you to notice
I'm in TextEdit right now
and I have JavaScript
for Automation code
written in a document.
So using that Automator
workflow as a service attached
to this hotkey, I can
run this, code executing
from the TextEdit IDE
that I love so much,
that I've created using
JavaScript for Automation.
We get our result, we
could copy to the clipboard
but not right now, thank you.
And so, that's amazing.
I just run JavaScript for
Automation from TextEdit,
and this is to show that
you can use JavaScript
for Automation how you'd
like, where you'd like.
It's extraordinarily extensible
and it's extraordinarily
flexible.
And as we mentioned before,
you can use JavaScript
for Automation from Terminal
using the osascript command
line tool.
So, let's look at an example.
We're going to be calling the
osascript command line tool
with a couple flags.
We're going to be using
the JavaScript language.
We want our result to print
a source, and then we pass
in some JavaScript to run.
This is what I talked
about before.
We can display an alert, so
imagine this could be a part
of a bash process at the
end that'll show us an alert
when our process
is about to finish.
So if I run this, our
alert was displayed just
like we wanted, which is great.
We get our result, we could
react accordingly if we wanted.
And another really
exciting new feature
of osascript is an
interactive mode.
So a variety of programming
languages, like Ruby and Python,
have interactive
modes that allow you
to interactively program
in those environments,
and now this is possible
from osascript.
So, to do so I'm going
to call osascript,
our language is JavaScript, and
I'm going to pass the -i flag
to say I'm going in
interactive mode.
Now to demo this
interactive mode,
I'm going to be interacting
with Safari.
So, I'm going to create
a variable and set it
to the Safari application, we
see that Safari was activated
for us, making it a
little better to see.
Then we can do things
like create a reference
to the first window.
Let's say we want to
get that window's name
and simply call the name
property as a function.
Favorites, like we
expect, that's great.
This is a lot of fun, but
let's spice it up a bit.
Let's create a new tab
and add that to the window
in real-time, interactively.
First, we'll create a new tab,
and we'll have the
URL be apple.com.
Then, to actually create this
in the application I'm going
to push it onto the
window's tabs element array.
And, before I do though, I know
that I want to bring the tab
to the front when I do this,
and I can't quite remember,
I think it's the front
tab or there's a property
that I can summon a window
to bring it to the front.
So, we're interacting
with this live,
before I do this let's make
sure that I know what I'm doing.
So I'm going to bring
up Script Editor
and I'm going access the
Library window and let's see,
we'll access Safari
scripting Dictionary.
Great. Okay, we'll look
at the Safari suite.
Yes, window, thank you,
ah, the currentTab,
not the frontTab, excuse me.
So now that I have this property
I know exactly how to create,
how to set the tab
to the current tab,
bringing it to the front.
So if I push out of the
array and bring the tab
to the front we've just now
live, interactively added a tab
to this window, and had a whole
lot of fun in the process.
[ Applause ]
And beyond being a cool way
to interact with applications,
this is phenomenal for
exploring their scriptability,
for learning JavaScript
for Automation.
It's a great tool to have.
All right.
So we've covered what JavaScript
for Automation is, how it works,
how you can use it and
where you can use it.
To summarize, JavaScript
for Automation is built
on JavaScriptCore, a modern
JavaScript environment
and it's built on the Open
Scripting Architecture,
which has been part of the
Mac OS since before OS X.
JavaScript for Automation has
been integrated system-wide
at all layers, from high-level
application scripting,
to system services, to
use in the terminal,
and accessing system APIs.
And as you saw, JavaScript
for Automation is flexible,
it allows you to
accomplish tasks
and solve problems the
best way that you see fit.
So, our call to action
today is go out
and script the scriptable
applications on your Mac.
For app developers, make
your applications scriptable
so other people can script
them and do amazing things.
And make sure to tell others
to make their applications
scriptable if it isn't already.
We have a phenomenal ecosystem,
we're really excited for more
and more applications
to join it every day.
To check out documentation,
make sure to go
to developer.apple.com,
for the JavaScript
for Automation release notes,
as well as some sample code.
We are having a phenomenal time
with this great new feature,
we cannot wait for you
to get your hands on it.
Thank you very much.
[ Applause ]