A script, using the new JSContext in OO 5.3 Pro, which can be run (on the macOS side) from Script Editor, Keyboard Maestro etc.
USE
- select one or more rows (at one or more levels) in an OO Outline
- run the script to extend the selection to all items at the same level(s) right across the outline.
TESTING ON MACOS
To test on macOS:
- Copy the whole of the source code below, and paste it into Script Editor,
- check that the Script Editor language selector (top left) is set to JavaScript
- check that an outline is open in OO, and run.
The selection will be extended, and a list of selected topic texts will be returned.
(() => {
'use strict';
// ooJSContext :: () -> String
const ooJSContext = () => {
// Left :: a -> Either a b
const Left = x => ({
type: 'Either',
Left: x
});
// Right :: b -> Either a b
const Right = x => ({
type: 'Either',
Right: x
});
// Just :: a -> Just a
const Just = x => ({
type: 'Maybe',
Nothing: false,
Just: x
});
// Nothing :: () -> Nothing
const Nothing = () => ({
type: 'Maybe',
Nothing: true,
});
// Node :: a -> [Tree a] -> Tree a
const Node = (v, xs) => ({
type: 'Node',
root: v, // any type of value (but must be consistent across tree)
nest: xs
});
// bindLR (>>=) :: Either a -> (a -> Either b) -> Either b
const bindLR = (m, mf) =>
m.Right !== undefined ? (
mf(m.Right)
) : m;
// concatMap :: (a -> [b]) -> [a] -> [b]
const concatMap = (f, xs) =>
xs.length > 0 ? [].concat.apply([], xs.map(f)) : [];
// iterateUntil :: (a -> Bool) -> (a -> a) -> a -> [a]
const iterateUntil = (p, f, x) => {
const go = x => p(x) ? x : [x].concat(go(f(x)));
return go(x);
};
// levels :: Tree a -> [[a]]
const levels = tree =>
map(xs => map(x => x.root, xs),
iterateUntil(
xs => xs.length < 1,
xs => concatMap(x => x.nest, xs), [tree]
)
);
// nub :: [a] -> [a]
const nub = xs => nubBy((a, b) => a === b, xs);
// nubBy :: (a -> a -> Bool) -> [a] -> [a]
const nubBy = (p, xs) => {
const mbx = xs.length > 0 ? (
Just(xs[0])
) : Nothing();
return mbx.Nothing ? [] : [mbx.Just].concat(
nubBy(p, xs.slice(1)
.filter(y => !p(mbx.Just, y)))
);
};
// map :: (a -> b) -> [a] -> [b]
const map = (f, xs) => xs.map(f);
// showJSON :: a -> String
const showJSON = x => JSON.stringify(x, null, 2);
// unlines :: [String] -> String
const unlines = xs => xs.join('\n');
// OO ----------------------------------------------------------------
// fmapPureOO :: (OOItem -> a) -> OOItem -> Tree OOItem
const fmapPureOO = (f, item) => {
const go = x => Node(f(x),
x.hasChildren ? (
x.children.map(go)
) : []);
return go(item);
};
// MAIN --------------------------------------------------------------
const
editor = document.editors[0],
selns = editor.selectedNodes,
lrNewSeln = bindLR(
selns.length > 0 ? Right(
nubBy(
(a, b) => a === b,
map(x => x.level, selns)
)
) : Left('Nothing selected in OmniOutliner'),
xs => {
const levelNodes = levels(
fmapPureOO(
x => editor.nodeForItem(x),
rootItem
)
);
return Right(unlines(concatMap(
iLevel => {
const ts = levelNodes[iLevel];
return (
editor.select(ts, true),
unlines(map(x => x.object.topic, ts))
);
},
xs
)));
}
);
return lrNewSeln.Right || lrNewSeln.Left;
};
return Application('OmniOutliner')
.evaluateJavascript(
'(' + ooJSContext + ')()'
);
})();