A script to toggle checkboxes, and checkbox state, in selected rows of a Bike document:
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: