Good point. The same can be seen on stack overflow, where people ask an innocent JavaScript question and get overwhelmed with jQuery stuff. Fortunately, that’s happening less now.
Any better source which focuses on JXA?
I’m not aware of any books which deal with JXA specifically.
JXA has two parts:
- JavaScript (quite a small language, and the Mozilla pages are the best source)
- The Automation library which Apple makes available to the JXA JavaScript interpreter, which is mainly covered by the release notes.
(linked to here: JavaScript for Automation (JXA) Resources)
The methods and properties of the Automation
library object are:
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
and the whole set of things under ObjC you can initially ignore really – they allow access to external Apple library functions.
Better than my brilliant site?
Seriously, there’s very little stuff out there on JXA. Many people probably were frustrated and have abandoned this ship. If you search for JXA, you’ll always stumble on the same, few and old sources. And sometimes you’ll be lucky and find a true gem helping tremendously with a problem.
OTOH, JXA differs, at its core, not very much from JavaScript. So, any text on JavaScript should help you find your ways in JXA. The main hurdles in JXA are the sometimes weird (for lack of a stronger term) interactions with Apple’s underlying software. Like this whole business with Object Specifiers, unclear usage of files and folders in Finder (just try to avoid them) and lack of some functionality in the ObjC bridge, notably code blocks and callbacks.
And since @winter beat me to it: The Automation
library object contains a lot of useful stuff and some completely obscure (like block
and dict
). Most of that is only relevant if you want to interface with Apple frameworks like PDFKit
and such.
Exactly – it’s just the standard macOS JavaScript interpreter – no difference at all, in fact, other than the presence of the Automation library in the global name-space.
I realized that this and a few other variations on it work when all the selected items have a value for the custom metadata field. But if the metadata field is blank/undefined in 1 or more selected DT3 records then the script fails. Why is that?
If the customMetaData
field is empty, i.e. there’s no CMD defined for this record, then ...customMetaData()
probably returns undefined
. Your code doesn’t check for this, but tries to always access the mdattorneyemail1
field of the return value. You could do something like
const CMD = app.selectedRecords[i].customMetaData();
if (CMD && CMD.mdattorneyemail1) {
// do something with CMD.mdattorneyemail1
} else {
// do something else: Nothing, write a log message, have an alert popup
}
Alternatively, you could use the method getCustomMetadata
instead of accessing the property directly.
Hence the .flatMap in the code shown earlier:
.flatMap(x => { // Execute the following lines for each element "x" of this Array
const v = x.mdattorneyemail1; //get the value of the custommetadata field AttorneyEmail1
return v ? [v] : []; //return a one-element array or an empty array nothing. The latter will be removed by flatMap, so that the final array only contains data for the non-empty metadata fields
})
which discards lines which return only an undefined
(Boolean false
) value for x.mdattorneyemail1
But what if customMetaData()
returns undefined
?
Same list-wrapped return value pattern, for example:
(() => {
"use strict";
const app = Application("DEVONthink 3");
return app.selectedRecords().flatMap(
record => {
const customMeta = record.customMetaData;
return customMeta.exists() ? (() => {
const v = customMeta().mdattorneyemail1;
return v ? [v] : [];
})() : [];
}
);
})();
Seems more robust.
and, of course, it can be variously reformulated, e.g.
(() => {
"use strict";
const app = Application("DEVONthink 3");
return app.selectedRecords().flatMap(record => {
const
customMeta = record.customMetaData,
v = customMeta.exists()
? customMeta().mdattorneyemail1
: false;
return Boolean(v) ? [v] : [];
});
})();
That works well! Huge thanks to both of you