Here FWIW, is a version composed from generic functions in JavaScript for Automation.
(This version is in ES6 JS, so compatible only with Sierra onwards. You can, however, get a (Yosemite onwards) ES5 version by pasting the source into the Babel JS Repl at babeljs.io/repl/ )
(() => {
'use strict';
// (++) :: [a] -> [a] -> [a]
const append = (xs, ys) => xs.concat(ys);
// comparing :: (a -> b) -> (a -> a -> Ordering)
const comparing = f =>
(x, y) => {
const
a = f(x),
b = f(y);
return a < b ? -1 : (a > b ? 1 : 0);
};
// concat :: [[a]] -> [a] | [String] -> String
const concat = xs =>
xs.length > 0 ? (() => {
const unit = typeof xs[0] === 'string' ? '' : [];
return unit.concat.apply(unit, xs);
})() : [];
// curry :: Function -> Function
const curry = (f, ...args) => {
const go = xs => xs.length >= f.length ? (f.apply(null, xs)) :
function () {
return go(xs.concat(Array.from(arguments)));
};
return go([].slice.call(args, 1));
};
// drop :: Int -> [a] -> [a]
// drop :: Int -> String -> String
const drop = (n, xs) => xs.slice(n);
// filter :: (a -> Bool) -> [a] -> [a]
const filter = (f, xs) => xs.filter(f);
// findIndex :: (a -> Bool) -> [a] -> Maybe Int
const findIndex = (p, xs) =>
xs.reduce((a, x, i) =>
a.nothing ? (
p(x) ? {
just: i,
nothing: false
} : a
) : a, {
nothing: true
});
// intercalate :: String -> [a] -> String
const intercalate = curry((s, xs) => xs.join(s));
// length :: [a] -> Int
const length = xs => xs.length;
// lines :: String -> [String]
const lines = s => s.split(/[\r\n]/);
// map :: (a -> b) -> [a] -> [b]
const map = (f, xs) => xs.map(f);
// maximumBy :: (a -> a -> Ordering) -> [a] -> a
const maximumBy = (f, xs) =>
xs.reduce((a, x) => a === undefined ? x : (
f(x, a) > 0 ? x : a
), undefined);
// replicate :: Int -> a -> [a]
const replicate = (n, x) =>
Array.from({
length: n
}, () => x);
// show :: Int -> a -> Indented String
// show :: a -> String
const show = (...x) =>
JSON.stringify.apply(
null, x.length > 1 ? [x[1], null, x[0]] : x
);
// splitOn :: a -> [a] -> [[a]]
// splitOn :: String -> String -> [String]
const splitOn = curry((needle, haystack) =>
typeof haystack === 'string' ? (
haystack.split(needle)
) : (function sp_(ndl, hay) {
const mbi = findIndex(x => ndl === x, hay);
return mbi.nothing ? (
[hay]
) : append(
[take(mbi.just, hay)],
sp_(ndl, drop(mbi.just + 1, hay))
);
})(needle, haystack));
// unconsMay :: [a] -> Maybe (a, [a])
const unconsMay = xs => xs.length > 0 ? {
just: [xs[0], xs.slice(1)],
nothing: false
} : {
nothing: true
};
// unlines :: [String] -> String
const unlines = xs => xs.join('\n');
// MMD TABLE -------------------------------------------------------------
// mmdTableFromTabbed :: String -> String
const mmdTableFromTabbed = s => {
const mbHeadTail = unconsMay(
map(splitOn('\t'), filter(x => length(x) > 0, lines(s)))
);
return mbHeadTail.nothing ? s : (() => {
const
ht = mbHeadTail.just,
t = ht[1];
return unlines(map(x => '|' + intercalate('\t|', x) + '\t|',
append(
[
ht[0],
replicate(
length(maximumBy(comparing(length), t)),
':--:'
)
],
t
)
));
})();
};
// CONVERSION OF A SELECTED DT SHEET TO MD -------------------------------
const
dt = Application('DEVONthink Pro'),
seln = dt.selection(),
mbRec = seln.length > 0 ? {
just: seln[0]
} : {
nothing: true
},
mbMD = mbRec.nothing ? (
mbRec
) : mbRec.just.type() === 'sheet' ? {
just: mmdTableFromTabbed(mbRec.just.richText.text())
} : mbRec;
return mbMD.nothing ? mbMD : (
dt.createRecordWith({
name: mbRec.just.name() + '.md',
type: 'markdown',
content: mbMD.just
}, { in: dt.currentGroup()
}),
mbMD.just
);
})();