Accordance.app :: Get [Name](Address) script

A first draft of a script which (on this system at least) copies both Name and Address from the selection in Accordance.app (for reading Biblia Hebraica, Septuagint, Mishna etc etc).

A couple of notes:

  • Paste into the Get Address script field (no need for a separate Get Name script)
  • Take care to copy the whole script from the opening comment line //JavaScript (needed by Hook to identify the language), down to: })();
//JavaScript
(() => {
    'use strict';

    // Rob Trew 2019
    // Ver 0.02


    // Use accord:// scheme (better for local links)
    // rather than https://accordance.bible/link
    // (better for internet sharing)
    const blnPreferAccordScheme = true;


    // main :: IO ()
    const main = () => {
        const
            se = Object.assign(Application('System Events'), {
                includeStandardAdditions: true
            }),
            procs = se.applicationProcesses.where({
                name: 'acord'
            });
        return either(alert('Accordance link'))(
            x => blnPreferAccordScheme ? (
                x.replace(
                    'https://accordance.bible/link/',
                    'accord://'
                )
            ) : x
        )(
            bindLR(
                0 < procs.length ? (
                    Right(procs.at(0).windows)
                ) : Left('Accordance application not found.')
            )(ws => bindLR(
                0 < ws.length ? (
                    Right(ws.at(0))
                ) : Left('No windows found for Accordance')
            )(w => Right(
                ('[' + apList([k => (
                    menuItemClicked('acord')(['Edit', 'Copy As', k]),
                    delay(0.1),
                    se.theClipboard()
                )])(['Reference', 'Location URL']).join('](') + ')')
            ))));
    };

    // -------------JavaScript for Automation--------------

    // alert :: String -> String -> IO String
    const alert = title => s => {
        const
            sa = Object.assign(Application('System Events'), {
                includeStandardAdditions: true
            });
        return (
            sa.activate(),
            sa.displayDialog(s, {
                withTitle: title,
                buttons: ['OK'],
                defaultButton: 'OK'
            }),
            s
        );
    };

    // Click an OS X app sub-menu item
    // 2nd argument is an array of arbitrary
    // length (exact menu item labels, giving full path)

    // menuItemClicked :: String -> [String] -> IO Bool
    const menuItemClicked = strAppName => lstMenuPath => {
        const intMenuPath = lstMenuPath.length;
        return intMenuPath > 1 ? (() => {
            const
                appProcs = Application('System Events')
                .processes.where({
                    name: strAppName
                });
            return appProcs.length > 0 ? (
                Application(strAppName)
                .activate(),
                lstMenuPath.slice(1, -1)
                .reduce(
                    (a, x) => a.menuItems[x].menus[x],
                    appProcs[0].menuBars[0].menus.byName(
                        lstMenuPath[0]
                    )
                )
                .menuItems[lstMenuPath[intMenuPath - 1]].click(),
                true
            ) : false;
        })() : false;
    };


    // GENERIC FUNCTIONS ----------------------------
    // https://github.com/RobTrew/prelude-jxa

    // Left :: a -> Either a b
    const Left = x => ({
        type: 'Either',
        Left: x
    });

    // Right :: b -> Either a b
    const Right = x => ({
        type: 'Either',
        Right: x
    });

    // apList (<*>) :: [(a -> b)] -> [a] -> [b]
    const apList = fs =>
        // The sequential application of each of a list
        // of functions to each of a list of values.
        xs => fs.flatMap(
            f => xs.map(f)
        );

    // bindLR (>>=) :: Either a -> (a -> Either b) -> Either b
    const bindLR = m => mf =>
        undefined !== m.Left ? (
            m
        ) : mf(m.Right);

    // either :: (a -> c) -> (b -> c) -> Either a b -> c
    const either = fl => fr => e =>
        'Either' === e.type ? (
            undefined !== e.Left ? (
                fl(e.Left)
            ) : fr(e.Right)
        ) : undefined;

    // MAIN ---
    return main();
})();
1 Like

Updated above to allow choice between two Accordance url schemes

  1. accord:// (better for local, Hook etc use – jumps straight to point in text with single click.
  2. https://accordance... (better for internet sharing of references, but jumps first to a browser page, requiring a second click to get to the passage in a local Accordance.app session).

See http://accordancefiles2.com/helpfiles/Win12/content/topics/05_dd/using_links_common_tasks.htm