How access Standard Additions in a script?


#1

Is there a way to bring the Standard Additions into an Omni Automation JavaScript? The usual way in JXA is:

a = Application.currentApplication();
a.includeStandardAdditions = true;

then, for instance:

a.beep();

However, in the Omni Outliner automation console

Application.currentApplication()

doesn’t work (even though that call is shown in this omnigroup-automation.com example (search for “example”). The value of

app

seems to be the application. (Is that what I expect Application.currentApplication() to return?)

app.includeStandardAdditions = true;

doesn’t give an error, but, for example,

app.beep
app.theclipboard
app.currentDate

are undefined.


#2

OmniJS is a completely different embedding of a JSContext, and doesn’t expose JXA’s Automation object at all.

You won’t be able to evaluate Standard Additions code in the omniJS interpreter, but you can evaluate omniJS source code from JXA, using the OO.evaluate(strSource) method, and get a return value back to JXA, so its possible to write scripts in which JXA and omniJS cooperate.

PS a useful way of shining a torch around a new or unfamiliar JS interpreter, and seeing what interfaces are available in its global namespace, is to evaluate the following snippet:

Object.getOwnPropertyNames(this)

The landscape will look very different in each of the following, for example:

  • A browser
  • JXA
  • The OmniJS interpreter in each different Omni app

#3

Footnote:

This whole tree of objects is peculiar to JXA – you won’t find it in an Omni JSContext:

Automation
    Application
        currentApplication
    Library
    ObjC
        $
        Ref
            equals
        bindFunction
        block
        castObjectToRef
        castRefToObject
        deepUnwrap
        dict
        import
        interactWithUser
        registerSubclass
        super
        unwrap
        wrap
    ObjectSpecifier
    Path
    Progress
    delay
    getDisplayString
    initializeGlobalObject
    log

Generated by the following (JXA only) code:

(() => {
    'use strict';

    // GENERIC FUNCTION ------------------------------------------------------

    // elem :: Eq a => a -> [a] -> Bool
    const elem = (x, xs) => xs.indexOf(x) !== -1;

    // INDENTED OUTLINE OF OBJECT KEYS ---------------------------------------
    const
        lstSeen = ['Automation', 'name', 'prototype', '__private__'],
        keyTree = (o, strIndent) => {
            const
                indent = strIndent || '',
                ks = Object.getOwnPropertyNames(o)
                .filter(k => (
                    elem(k, lstSeen) ? false : (
                        lstSeen.push(k),
                        true
                    )
                ))
                .sort();

            return ks.length ? (
                ks.map(k =>
                    indent + k + '\n' + keyTree(o[k], indent + '    ')
                )
                .join('')
            ) : '';
        };
        
    // MAIN 
    const
        a = Application.currentApplication(),
        sa = (a.includeStandardAdditions = true, a),
        strClip = 'Automation\n' + keyTree(Automation, '    ');

    return (
        sa.setTheClipboardTo(strClip),
        strClip
    );
})();


#4

I would love an example showing how to evaluate OmniJS from JXA with a return value. (I know how to execute OmniJS source from JXA via a URL, but that doesn’t provide a return value.)

I see what you are saying, and I realize how naïve my post was, but I can’t quite put the pieces together. In particular, what (where) is OO.evaluate? A very simple example for which I would want to get the result back to JXA would be:

document.editors[0].selectedNodes[0].object.topic

#5

This is very cool, and of particular interest to me, because getting a class tree is my favorite exercise when working with a new language/environment (starting with Smalltalk-76!!).


#6

The JXA interface for the omniOutliner application object has an .evaluateJavascript(strCode) method.

As you probably know, JS functions have a .toString() method (and can even be coerced to their source code by simple string concatenation), so you can define the main omniJS function in your JXA code, and then pass a stringification of it into the omniJS interpreter with .evaluateJavascript(strCode).

That method can return a string value.

There are some notes here on the use of the omnigraffle instance of .evaluateJavascript:


#7

Got it! In a JXA script:

    Application('OmniOutliner').
       evaluateJavascript(
         "document.editors[0].selectedNodes[0].object.topic"
         )