Yep, I think that’s it.
Here’s a first rough draft, so there may be some glitches to discover.
It aims to handle @repeat tags in the style of the earlier script at:
You could test it initially from Script Editor, using dummy data, and ⌘Z Undo,
and later assign to a keystroke, or put it in the TaskPaper script menu
JavaScript for Automation Source:
(make sure that you scroll down to fully copy all of the lines)
// First ROUGH draft of @repeat tag handling on the model of an earlier script at:
// http://www.hogbaysoftware.com/wiki/DueDates
// TEST with a combination of dummy data and the UNDO key (Command-Z)
// Rob Trew @complexpoint 2016-05-06
(function () {
'use strict';
// TASKPAPER CONTEXT
function TaskPaperContext(editor, options) {
// What @repeat(UNIT:Quantity) pair do we have here ?
// unitQuantParsed :: String -> maybe String
// -> {unit: maybe String, quant: maybe Integer}
function unitQuantParsed(strSerial, strMaybeDelimiter) {
var lstParts = strSerial.split(strMaybeDelimiter || ':'),
lngParts = lstParts.length,
strUnit = lngParts > 0 ? lstParts[0] : undefined;
return {
unit: strUnit,
nameInterval: strUnit ? nameInterval(strUnit) : undefined,
quant: !isNaN(lngParts > 1 ? (
lstParts[1]
) : undefined) ? (
parseInt(lstParts[1], 10)
) : undefined
};
}
// Is this a unit like day, month, year etc
// or a calendar Name like Friday, May etc ?
// Weekday names -> 7, Month names -> c. 365
// nameInterval :: String -> Integer
function nameInterval(strDayOrMonthName) {
return isNaN(DateTime.parse(strDayOrMonthName)) ? (
undefined
) : (
DateTime.parse('next ' + strDayOrMonthName) -
DateTime.parse('last ' + strDayOrMonthName)
) / 172800000; // 2 days of milliSeconds
}
// Parseable expression for dateTime of next recurrence ?
// nextExpression :: Item -> String
function nextExpression(item) {
var strRepeat = item.getAttribute('data-repeat'),
dctRepeat = unitQuantParsed(strRepeat),
// If the task has a @due tag, then all calculations are done
// relative to that date.
// Otherwise, they will be calculated relative to today.
dteAnchor = DateTime.parse(
item.getAttribute('data-due') || 'today'
);
if (dteAnchor && dctRepeat.unit) {
var lngInterval = dctRepeat.nameInterval;
if (lngInterval) {
var dteNamed = DateTime.parse(dctRepeat.unit),
strBase = (dteNamed < dteAnchor) ? (
'next ' + dctRepeat.unit
) : DateTime.format(dteNamed),
lngSkipped = dctRepeat.quant ? (
dctRepeat.quant - 1
) : 0;
return lngSkipped ? (
(strBase + ' +' + lngSkipped.toString()) +
(lngInterval === 7 ? ' weeks' : (
lngInterval > 360 ? ' years' : ' days'
))
) : strBase;
} else return DateTime.format(dteAnchor) + ' +' +
(dctRepeat.quant || 1) + ' ' + dctRepeat.unit
} else return undefined;
}
// MAIN
var outline = editor.outline,
lstUpdateable = outline.evaluateItemPath(
'//@repeat and (@done or not @due)');
if (lstUpdateable.length > 0) {
var result = undefined;
outline.groupUndoAndChanges(function () {
result = lstUpdateable
.map(function (item) {
var strNext = nextExpression(item);
if (strNext) {
var dteNext = DateTime.parse(
strNext
);
if (!isNaN(dteNext)) {
item.removeAttribute(
'data-done'
);
item.setAttribute(
'data-due',
DateTime.format(
dteNext
)
);
} else return 'Not parsed: ' + item.bodyString;
} else return 'Not parsed: ' + item.bodyString;
return strNext;
});
});
return result;
}
};
// JAVASCRIPT FOR AUTOMATION CONTEXT
var tp3 = Application('com.hogbaysoftware.TaskPaper3'),
ds = tp3.documents,
d = ds.length ? ds[0] : (
ds.push(tp3.Document()), ds[0]
);
return d.evaluate({
script: TaskPaperContext.toString(),
withOptions: {
delimiter: ':'
}
});
})();
This seems to work perfectly.
2 Likes