For OmniGraffle Test version 7.4 test (v179.5 r290241)
OmniJS, already fast and cross-platform, is also becoming useful.
FWIW, as a kind of tester’s journal entry, here is an experiment in which an omniJS function is called by JavaScript for Automation, and uses the new OGOutlineNode interface to return to JXA a JSON text-nest version of the outline text of a nested diagram in the selected canvas.
(omniJS is still being finished and polished in the workshop - so this draft uses an interim hack for getting a value from omniJS back to JXA – Omni is already preparing something much better).
(() => {
'use strict';
// Rough draft .003 - purely illustrative no claims or guarantees
// use with caution, and not on real data.
// Rob Trew June 20 2017
// GENERIC JS FUNCTIONS --------------------------------------------------
// A list of functions applied to a list of arguments
// <*> :: [(a -> b)] -> [a] -> [b]
const ap = (fs, xs) => //
[].concat.apply([], fs.map(f => //
[].concat.apply([], xs.map(x => [f(x)]))));
// An integer prepended to the argument list yields an indentation level
// show :: a -> String
const show = (...x) =>
JSON.stringify.apply(
null, x.length > 1 ? [x[0], null, x[1]] : x
);
// JAVASCRIPT FOR AUTOMATION FUNCTIONS ---------------------------------
// menuItemClick :: String -> [String] -> UI ()
const menuItemClick = (strAppName, lstMenuPath) => {
const intMenuPath = lstMenuPath.length;
if (intMenuPath > 1) {
const appProcs = Application('System Events')
.processes.where({
name: strAppName
}),
procApp = appProcs.length ? appProcs[0] : undefined;
if (procApp) {
Application(strAppName)
.activate();
lstMenuPath.slice(1, -1)
.reduce((a, x) => a.menuItems[x].menus[x],
procApp.menuBars[0].menus.byName(lstMenuPath[0]))
.menuItems[lstMenuPath[intMenuPath - 1]].click();
}
}
};
// OMNIGRAFFLE JXA FUNCTIONS ---------------------------------------------
// evaluateOmniJS :: (Options -> OG Maybe JSON String) -> {KeyValues}
// -> Maybe JSON String
const evaluateOmniJS = (f, dctOptions) => {
const
a = Application.currentApplication(),
sa = (a.includeStandardAdditions = true, a);
ap([sa.openLocation, sa.setTheClipboardTo], //
['omnigraffle:///omnijs-run?script=' +
encodeURIComponent(
'(' + f.toString() + ')(' +
(dctOptions && Object.keys(dctOptions)
.length > 0 ? show(dctOptions) : '') + ')'
)
]);
// Possible harvest of return value from canvasbackground.userData
const
og = Application('OmniGraffle'),
ws = og.windows,
mw = ws.length > 0 ? {
just: ws.at(0),
nothing: false
} : {
nothing: true
},
mResult = mw.nothing ? mw : (() => {
const w = mw.just;
return w.name()
.indexOf('Automation Console') === 0 ? ({
nothing: true,
msg: 'Front window was Automation Console'
}) : (() => {
const v = w.canvas()
.canvasbackground.userDataItems.byName('omniJSON')
.value()
return (v === undefined || v === null) ? ({
nothing: true,
msg: "No JSON found in " +
".canvasbackground.userDataItems.byName('omniJSON')"
}) : {
just: v,
nothing: false
}
})();
})();
return mResult.nothing ? mResult : (
mw.just.canvas()
.canvasbackground.userDataItems.byName('omniJSON')
.value = null,
mResult
);
};
// ogFrontDoc :: {useExisting : Bool, templateName: String} -> OG.Document
const ogFrontDoc = dctOptions => {
const
options = dctOptions || {},
og = Application('OmniGraffle'),
optTemplate = options.templateName,
xs = og.availableTemplates(),
strTemplate = (optTemplate && elem(optTemplate, xs)) ? (
optTemplate
) : xs[0],
ds = og.documents,
d = options.useExisting && ds.length > 0 ? (
ds.at(0)
) : (() => {
return (
ds.push(og.Document({
template: strTemplate
})),
ds.at(0)
);
})();
return (
og.activate(),
d
);
};
// AN OMNIJS FUNCTION to be run with evaluateOmniJS() (above) ----------
// treeJSON :: OG () -> JSON String
const treeJSON = () => {
// show :: a -> String
const show = (...x) =>
JSON.stringify.apply(
null, x.length > 1 ? [x[0], null, x[1]] : x
);
// textNest :: OGoutlineNodes -> Node {text: String, nest:[Node]}
const textNest = xs =>
xs.length ? xs.map(x => ({
text: x.graphic.text,
nest: x.children.length > 0 ? (
textNest(x.children)
) : []
})) : [];
const
cnv = document.windows[0].selection.canvas,
strJSON = show(textNest(
cnv.outlineRoot.children
));
// Save a return value for JXA to find in Canvas background user-data
return (
cnv.background.setUserData('omniJSON', strJSON),
strJSON
);
};
// TEST ------------------------------------------------------------------
const
d = ogFrontDoc({
useExisting: true
}),
maybeReturnValue = evaluateOmniJS(treeJSON);
// // Open the OG Script Console
//menuItemClick('OmniGraffle', ['Automation', 'Show Console']);
return maybeReturnValue.nothing ? (
undefined
) : JSON.stringify(JSON.parse(maybeReturnValue.just), null, 2);
})();