Following the theme of @Phil’s earlier @start date picker, here is a draft of a general date-picker script for TaskPaper 3.
pickDateTime.app.zip 4 (52.7 KB)
USE:
- Adds or updates a datetime (for any @tag containing or to the left of the cursor)
- Adds the specified default tag to lines in which no tag has yet been typed.
To add a date:
- type the first part of a tag, e.g.
@due
(no need for parentheses) - launch the script and pick a date from the calendar
To alter a date:
- place the selection cursor in or to the right of the dated tag
- launch the script and pick a different date
(The clock is mainly for display of any existing time. You can drag the hands to change the time if you want to, but I personally prefer to change the time part of tags manually. The date picker is mainly useful for the calendar and the days of the week)
(Doesn’t need any particular launcher app like Keyboard Maestro, LaunchBar etc, though it can, of course, be used with any of them).
INSTALLATION:
The JavaScript for Automation source (below) needs to be saved from Script Editor as an .app file (rather than .scpt),
Save As > File Format > Application
with: ‘‘Stay open after run handler’’ checked
and then run from the Script Menu in TaskPaper
As described here: http://guide.taskpaper.com/using_scripts.html 40
Once the .app file is in the ~/Library/Scripts/Applications/TaskPaper folder, it can be run not only from the TaskPaper 3 script menu:
but also from any launcher like LaunchBar (add ~/Library/Scripts/Applications/TaskPaper to the LaunchBar index) or Keyboard Maestro
CUSTOMIZATION:
There are two options which can be adjusted at the end of the script:
- The tag to add if the selected line does not yet have any tags
- the default time to use when creating date tags (default is ‘00:00’ which results in no time being displayed in the tag)
{
defaultTag: 'start', // if no existing tag (without '@' or 'data-' prefix)
defaultTime: '00:00' // if no existing time (24:00 format)
}
JavaScript for Application source:
(function (dctOptions) {
'use strict';
// Ver 0.2 (displays text of selected line in dialog)
// TASKPAPER CONTEXT
// selectedTag :: editor -> maybe String
function selectedTag(editor) {
'use strict'
// tagPositions :: item -> [{location: Int, tagname: String}]
function tagPositions(item) {
var s = item.bodyHighlightedAttributedString;
for (var lng = s.length, rng = {
location: 0
}, i = 0, lst = [], dct; i < lng;) {
dct = s.getAttributesAtIndex(i, rng);
if (dct.tagname) {
lst.push({
tagname: dct.tagname,
location: rng.location
});
}
i += rng.length;
}
return lst;
}
// find :: (a -> Bool) -> [a] -> Maybe a
function find(f, xs) {
for (var i = 0, lng = xs.length; i < lng; i++) {
if (f(xs[i])) return xs[i];
}
return undefined;
}
// reverse :: [a] -> [a]
function reverse(xs) {
var sx = xs.slice(0);
return sx.length > 1 ? sx.reverse() : sx;
}
// List the positions of all the tags in this item
var selection = editor.selection,
item = selection.startItem,
strType = item.getAttribute('data-type'),
strExceptTags = (strType === 'task' ? '- ' : '') +
item.bodyContentString + (strType === 'project' ? ':' : ''),
iSelnOffset = selection.end - editor.getLocationForItemOffset(
item, 0
),
lstTagPosns = tagPositions(item);
// work back from the end of the selection to the first tag
// which starts before that location
if (lstTagPosns.length > 0) {
var maybeTag = find(function (x) {
return (x.location < iSelnOffset);
}, reverse(lstTagPosns));
return {
content: strExceptTags,
tag: maybeTag ? {
tag: maybeTag.tagname,
dateTime: item.getAttribute(
maybeTag.tagname,
Date
) || undefined
} : undefined
}
} else return {
content: strExceptTags,
tag: undefined
};
}
// Set a tag/value pair in the first item of the selection
function setTagDate(editor, options) {
var strTag = options.tag,
dteDate = options.when,
item = editor.selection.startItem;
if (strTag && dteDate && item) {
item.setAttribute(
strTag,
DateTime.format(dteDate)
);
}
}
// JAVASCRIPT FOR AUTOMATION CONTEXT
ObjC.import('AppKit');
ObjC.import("Cocoa");
// datePicker :: $.NSDate -> $.NSDatePicker
function datePicker(initialDate) {
var oPicker = $.NSDatePicker.alloc.initWithFrame(
$.NSMakeRect(0, 0, 100, 100)
);
oPicker.setDatePickerStyle(
$.NSClockAndCalendarDatePickerStyle
);
oPicker.setDatePickerElements(
$.NSYearMonthDayDatePickerElementFlag |
$.NSHourMinuteDatePickerElementFlag
);
oPicker.setDateValue(initialDate);
return oPicker;
}
// pickerView :: $.NSDatePicker -> ($.NSView, $.NSDatePicker)
function pickerView(oPicker) {
var dctSize = oPicker.fittingSize,
oView = $.NSView.alloc.initWithFrame(
$.NSMakeRect(0, 0, 100, 200)
);
oView.setFrameSize(dctSize);
oPicker.setFrameSize(dctSize);
oView.setSubviews($.NSArray.arrayWithObjects(oPicker));
return {
picker: oPicker,
view: oView
};
}
// pickerAlert :: $.NSView -> $NSDatePicker String -> String -> maybe JS Date
function alertResult(dctPickerView, strVerb, strTagName, strText) {
var oAlert = $.NSAlert.alloc.init;
oAlert.setMessageText(strVerb + ' @' + strTagName +
' tag in TaskPaper 3');
oAlert.setInformativeText(
strText
);
oAlert.addButtonWithTitle('OK');
oAlert.addButtonWithTitle('Cancel');
oAlert.setAccessoryView(dctPickerView.view);
oAlert.icon = $.NSImage.alloc.initByReferencingFile(sa.pathToResource(
'TaskPaperAppIcon.icns', {
inBundle: 'Applications/TaskPaper.app'
})
.toString());
return (oAlert.runModal === $.NSAlertSecondButtonReturn) ? (
undefined
) : ObjC.unwrap(dctPickerView.picker.dateValue);
}
// MAIN
// 1. Selected tag ? With date-time value ?
var tp3 = Application("com.hogbaysoftware.TaskPaper3"),
ds = tp3.documents,
d = ds.length ? ds[0] : undefined;
if (d) {
var dctSelected = d.evaluate({
script: selectedTag.toString()
}),
dctTagDate = dctSelected.tag || {
tag: 'data-' + dctOptions.defaultTag
},
strDate = dctTagDate.dateTime,
// Existing date and time, or today, with time set to default
// (e.g. 00:00 see dctOptions.defaultTime below)
// if not specified
dteInitial = strDate ? new Date(strDate) : (function () {
var dte = new Date(),
lstTime = dctOptions.defaultTime.split(':');
if (lstTime.length > 1) {
dte.setHours(
isNaN(lstTime[0]) ? 0 : parseInt(
lstTime[0], 10
),
isNaN(lstTime[1]) ? 0 : parseInt(
lstTime[1], 10
), 0, 0
)
} else dte.setHours(0, 0, 0, 0);
return dte;
})();
// 2. User response to date picker showing default or selected tag
// with default or selected time
var a = Application.currentApplication(),
sa = (a.includeStandardAdditions = true, a);
a.activate();
var maybeDate = alertResult(
pickerView(
datePicker(dteInitial)
),
dctSelected ? (
(strDate ? 'Update' : 'Choose date for')
) : 'Create',
dctTagDate.tag.substr(5),
dctSelected.content
);
// 3. If user response, updated tag
if (maybeDate) {
d.evaluate({
script: setTagDate.toString(),
withOptions: {
tag: dctTagDate.tag,
when: maybeDate.toString()
}
});
tp3.activate();
}
sa.quit(); // Close the date-picker applet
}
})({
defaultTag: 'start', // if no existing tag (without '@' or 'data-' prefix)
defaultTime: '00:00' // if no existing time (24:00 format)
});