A brief, painful attempt to understand JSA

JSA is “JavaScript for Automation”, aka Apples attempt to embed JavaScriptCore into their Apple Event model.

The use case here is, roughly, “I know JavaScript and [love|like|tolerate] it, but I cannot under any circumstances be arsed to do anything useful in AppleScript, because AppleScript is easily one of the worst software platforms ever created by man”.

Seriously, AppleScript is extremely bad. I have in my life encountered a few people who loved it; and nearly 100% of them are not work-a-day programmers. AppleScript is purpose-built to confuse, annoy, and frustrate people who can chew through languages like Swift with little effort.

(I’m not trying to start a “you’re not a real programmer” thing; if you write a program – any program – you’re a real programmer! I’m just saying that the norms AppleScript adheres to are typically diametrically opposed the stuff we work-a-day programmers have deeply, deeply internalized as “normal”.)

Anyway! I decided rather than replace a bunch of light switches my wife has decided are no logner aesthetically pleasing, I thought I’d spend the morning trying to hack on some JSA. After all: how hard can it be? It’s JavaScript and I know JavaScript, and I have once or twice managed to fumble around with AppleScript until I accomplished something useful.

I can sum up the morning with the phrase, “Oh, for fuck’s sake”.

BUT FIRST, A DISCLAIMER: I am not a good AppleScript programmer. There are a lot of fundamental issues I have not internalized, because my primary interaction with AS and AE are “I have a single problem that needs fixing, and it’s been over a year since the last time I had to do this, and because my job involves stuffing my grey matter with other details, something has to go and it’s usually AppleScrcipt and Apple Events”. So should you stumble across this and want to hurl invective because clearly I don’t know shit about AppleScript, the answer will be “yeah, didn’t you read the disclaimer? I don’t know shit.”

Problem The First: The documentation is full of lies, half-truths, omissions, and using it is like having somene shout angrily at you in Romanian while they’re holding a sign that says “I’m speaking Spanish, honest!”

The basic interface to the Apple Events is what’s called a “dictionary” and it looks like this:

Screen Shot 2018-03-07 at 9.50.43 AM.png

A dictionary is composed of suites, classes, commands, properties, and some other stuff. So far, so good; we can grasp this. But, as pictured, that’s AppleScript, and we’re doing JavaScript. Since they’re different languages, they’ll appear differently, non?

Screen Shot 2018-03-07 at 9.52.46 AM.png

ok great. GREAT. So now we’ve started off with the premise that 2 entirely different languages are exactly the same! Neat. Here’s a way it’s neat:

Screen Shot 2018-03-07 at 9.55.04 AM.png

See how it says “properties”? OK, I can understand that. In JavaScript, the property of an object is accessed thus: objectName.propertyName (see here for more).

Except they’re not properties at all. Via osascript -il JavaScript:

var bbedit = Application('BBEdit')
=> undefined
> bbedit.id
=> [function anonymous] {
"prototype":{"constructor":[function anonymous]}
>> bbedit.id()
=> "com.barebones.bbedit"

So here we have a few lies. First, you’ll note that per the docs, ‘id’ is a property; it’s not, it’s a function. And it says it returns an integer, but it clearly returns a string. Yaaaaaay.

And to be clear, I’m not picking on BBedit/BareBones here. If you dig through the docs of numerous apps, it’s like this. My assumption is at this point that the toolchain simply doesn’t know how to create meaningful output for the two languages since AS, JS, and Obj-C all use the same template and are full of lies.

Still! Documentation that is wrong is almost worse than no documentation at all, because you lose time to doing what you are told is correct and wondering why it’s not working. Am I wrong? Did I skip a step? Is something else at play here? No, the docs are just wrong.

Problem The Second: If you haven’t internalized Apple Events, you’re probably fucked

The docs on Apple Events and AppleScript are surprisingly dense for a language intended for non-technical people. And in particular, the notion of singular and plural terms to denote the programmer-centric ideas of scalar vs list values.

So, how would you guess you’d get the source language of the frontmost window, by looking at this:

Screen Shot 2018-03-07 at 10.14.15 AM.png

If you click on ‘textDocuments’ you get to this:

Screen Shot 2018-03-07 at 10.15.12 AM.png

Hmm, ok, so, it’s CapitalCase like all the objects, so, do I need to create an instance of it? It inherits from Document and Item, so do I have all those methods? Why was it cased differently in the Application doc (textDocuments vs TextDocument). Singular? Plural? WHAT THE FUCK

[Yes I know I’m back to ‘the docs suck’. Bear with me.]

Anyway, to cut to the chase, here’s the answer:

> bbedit.textDocuments()
=> [Application("BBEdit").textDocuments.first]
>> bbedit.textDocuments()[0]
=> Application("BBEdit").textDocuments.first
>> bbedit.textDocuments()[0].sourceLanguage
=> Application("BBEdit").textDocuments.first.sourceLanguage
>> bbedit.textDocuments()[0].sourceLanguage()
=> "Python"

The answer here is, apparently, that an Application instance has a method called ‘textDocuments’ that returns a list whose sole element is the first text document object, and from there, you can call the NOT A MOTHERFUCKING PROPERTY sourceLanguage method.

You can also do this:

> bbedit.windows()[0].sourceLanguage()


Screen Shot 2018-03-07 at 10.21.30 AM.png

and as we saw above, an Application contains windows. Which one is better? Which one is correct? WHO FUCKING KNOWS. EVERYTHING IN THIS IS A FUCKING ANTIPATTERN.

The point here is, all this seems to do with how Apple Events works, and unless you know how it all makes sense, it’s maddening. Some paths don’t lead to where you want to go; that there are 2 ways to walk down the “application object model” is probably because BareBones are good at this shit. Another application whose “AOM” isn’t as well-made will probably lead you down false paths.

That’s all I have in 2 hours of futzing about; very little productive work but enough to write this dumb-ass blog post bitching about it. But I did so hopefully the next time I’ll remember to look here when I forget how to get the frontmost document.

Oh and don’t forget, always do

your_application_object.includeStandardAdditions = true;

so you can do things in the Standard Additions dictionary (display dialog, etc) from within your app.

Edited To Add: The point of the exercise today was to get the insertion point. WELL FUCK.

Leave a Reply

Please log in using one of these methods to post your comment:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s