Like FoldingText, iOS Drafts can run scripts written in Javascript.
We can write editor and text manipulation scripts which span the iOS ⇄ OS X divide, and run on both FoldingText and drafts, by writing a simple header which defines a FoldingText version of each of the 11 Drafts editor functions:
getText() getSelectedLineRange()
setText(string) getSelectedRange()
getSelectedText() setSelectedRange(start, length)
setSelectedText(string) getClipboard()
getTextInRange(start, length) setClipboard(string)
setTextInRange(start, length, string)
For example, this block selection script which runs in Drafts
can also run in FoldingText, if we include the header defining the Drafts functions in terms of the FT API:
// iOS DRAFTS and iOS 1Writer compatibility header for FT /////////
// 1Writer scripts must be preceded by the compatibility header at
// http://support.foldingtext.com/t/writing-scripts-which-run-on-both-foldingtext-and-ios-drafts-4/652/5?u=complexpoint
// Example function calls: strText = drafts.getText(); lstRange = drafts.getTextInRange(240,80);
// Ver 0.4 fixed selection range issue, enabling extension of selection to more than one blocks
// Ver 0.3 added drafts. prefix to functions to avoid namespace clashes in apps using any function name
// which happens to be the same as an AgileTortoise Drafts 4 function name.
function run() {
function fnFT(editor, opt) {
// iOS DRAFTS compatibility header for FT //
var oTree = editor.tree();
var drafts = {
getText: function () {
return oTree.text();
},
setText: function (strText) {
editor.setTextContent(strText);
return true;
},
getSelectedText: function () {
return editor.selectedText();
},
setSelectedText: function (strText) {
editor.replaceSelection(strText, 'around');
return true;
},
getTextInRange: function (iStart, iLength) {
return oTree.createRangeFromLocation(
iStart, iLength
).textInRange();
},
setTextInRange: function (iStart, iLength, strText) {
editor.replaceTextInRange(
oTree.createRangeFromLocation(iStart, iLength), strText
);
return true;
},
getSelectedLineRange: function () {
var oNode = editor.selectedRange().startNode,
rngLine = oTree.createRangeFromNodes(
oNode, 0, oNode, -1
);
return [rngLine.location(), rngLine.length()];
},
getSelectedRange: function () {
var lstSeln = [];
editor.selectedRanges().forEach(function (rng) {
lstSeln.push(rng.location());
lstSeln.push(rng.length());
});
return lstSeln;
},
setSelectedRange: function (iStart, iLength) {
editor.setSelectedRange(
oTree.createRangeFromLocation(iStart, iLength)
);
return true;
},
getClipboard: function () {
return Pasteboard.readString();
},
setClipboard: function (strText) {
Pasteboard.writeString(strText);
return true;
}
};
// iOS DRAFTS compatibility header for FT //
function overlap(lstA, lstB) {
// NOT IF THIS ENDS BEFORE THAT STARTS,
// OR STARTS AFTER THAT ENDS
return !(lstA[1] < lstB[0] || lstA[0] > lstB[1]);
}
// DRAFTS COMPATIBLE CODE VER 2.0 simpler approach to selecting the block:
function selnExtendToBlock() {
var rgxGap = /(\n{2,})/,
lstParts = drafts.getText().split(rgxGap),
lstSeln = drafts.getSelectedRange(),
iSelnStart = lstSeln[0],
iSelnEnd = iSelnStart + lstSeln[1],
lstBlocks = [],
strBlock, strGap,
iFrom = 0,
iTo, lngBlock;
// Find first overlap with a selection edge
for (var i = 0, lng = lstParts.length; i < lng; i += 2) {
strBlock = lstParts[i];
strGap = lstParts[i + 1] || '';
lngBlock = strBlock.length;
iTo = iFrom + lngBlock;
if (overlap([iSelnStart, iSelnEnd], [iFrom, iTo]))
lstBlocks.push({
txt: strBlock,
start: iFrom,
end: iFrom + lngBlock
});
iFrom = iTo + strGap.length;;
if (iFrom > iSelnEnd) break;
}
// extend selection to just after last gap
// and just before next gap
iSelnStart = lstBlocks[0].start;
drafts.setSelectedRange(iSelnStart, lstBlocks[lstBlocks.length - 1].end - iSelnStart);
return true;
}
return selnExtendToBlock();
}
var docsFT = Application("FoldingText").documents(),
varResult = docsFT.length && docsFT[0].evaluate({
script: fnFT.toString(),
withOptions: {}
});
return varResult;
}