Script :: Toggling checked ⇄ unchecked box in selected rows

A script to toggle checkboxes, and checkbox state, in selected rows of a Bike document:

checkBox003


As a JavaScript script

  • to test in Script Editor (with the language-selector at top left set to JavaScript)
  • and to assign to a keystroke with FastScripts or Keyboard Maestro

See: Using Scripts - Bike

Expand disclosure triangle to view JS Source
(() => {
    "use strict";

    // Cycle a set of prefix characters in selected
    // (non-empty) lines of Bike 1.3

    // Rob Trew @2022
    // Ver 0.04

    // --------------------- OPTION ----------------------
    // Which single-character prefixes to cycle ?
    // (Any space character is interpreted as a
    //  no-prefix stage in the cycle).
    const prefixCycle = "☐☑ ";

    // ------------ PREFIX CHARACTER TOGGLED -------------
    // main :: IO ()
    const main = () => {
        const
            bike = Application("Bike"),
            doc = bike.documents.at(0);

        return doc.exists() ? (() => {
            const
                selectedNonEmptyRows = doc.rows.where({
                    selected: true,
                    _not: [{
                        name: ""
                    }]
                });

            return Boolean(selectedNonEmptyRows.length) ? (
                bikeRowsPrefixCycled([...prefixCycle])(
                    selectedNonEmptyRows
                )
            ) : "No non-empty rows selected in Bike.";
        })() : "No documents open in Bike.";
    };

    // ------------------ PREFIX CYCLED ------------------

    // bikeRowsPrefixCycled :: [Char] -> Int ->
    // IO Bike Rows -> IO String
    const bikeRowsPrefixCycled = prefixList =>
        // Any selected non-empty rows in Bike cycled
        // to the the next prefix in prefixList
        selectedNonEmptyRows => {
            const
                n = selectedNonEmptyRows.length,
                plural = 1 < n ? "s" : "",
                [f, change] = (() => {
                    const
                        iPrefix = prefixList
                        .findIndex(
                            c => c === (
                                selectedNonEmptyRows
                                .at(0).name()[0]
                            )
                        ),
                        nextPrefix = prefixList[
                            -1 !== iPrefix ? (
                                (1 + iPrefix) % prefixList
                                .length
                            ) : 0
                        ];

                    return [
                        updatedLine(prefixList)(nextPrefix),
                        " " !== nextPrefix ? (
                            `Set ${nextPrefix}`
                        ) : "Cleared"
                    ];
                })();

            return (
                zipWith(row => s => row.name = s)(
                    selectedNonEmptyRows()
                )(
                    selectedNonEmptyRows.name().map(f)
                ),
                [
                    `${change} prefix`,
                    `in ${n} selected line${plural}.`
                ]
                .join("\n")
            );
        };


    // updatedLine :: [Char] -> Char -> String -> String
    const updatedLine = prefixes =>
        // A line in which the prefix has been
        // replaced by the next option in a cycle.
        // A space character in the prefixes list is
        // interpreted as no-prefix stage in the cycle.
        c => s => Boolean(s) ? (
            prefixes.includes(s[0]) ? (
                c + s.slice(1)
            ) : `${c} ${s}`
        ).trimStart() : `${c} `;


    // --------------------- GENERIC ---------------------

    // zipWith :: (a -> b -> c) -> [a] -> [b] -> [c]
    const zipWith = f =>
        // A list constructed by zipping with a
        // custom function, rather than with the
        // default tuple constructor.
        xs => ys => xs.map(
            (x, i) => f(x)(ys[i])
        ).slice(
            0, Math.min(xs.length, ys.length)
        );

    // MAIN
    return main();
})();

and as a Keyboard Maestro macro:

BIKE – Toggle a checked ⇄ unchecked box prefix in selected lines - Macro Library - Keyboard Maestro Discourse

@complexpoint Thanks! Great KM script to have!

1 Like