New/Improved Start Date Picker Macro


#1

I previously made a crude date picker for setting start dates. I called it crude because it relied on UI scripting, it only worked for a single task at a time, and the date picker UI was buggy and ugly. So, yea, crude.

Here is a new and improved version. All modifications are now done via code (no UI scripting), it can change start dates for multiple selected lines simultaneously, and it is prettier and easier to use than the original date picker.

Setup / Dependencies:
This macro relies on iNik’s Keyboard Marstro “Fancy Mac-Like HTML Prompts Library” here: https://forum.keyboardmaestro.com/t/fancy-mac-like-html-prompts-library/3361. You’ll need to download his library and put the html-elements folder in your ~/Library/Application Support/Keyboard Maestro/ folder.

Then download the Keyboard Maestro macro below that I made based on iNiks’s library. It provides a jquery date picker to be used for setting TaskPaper start dates. You will need to change the 3rd line of the Custom HTML Prompt action from “UserName” to your specific User Name. Set a keyboard shortcut for this macro to call it and change start dates of selected TaskPaper items.

Optional Setup:
If you want to use this macro to set due dates, you can change “start” to “due” in the 3rd to last line of the “Execute JavaScript for Automation” action.

By default, the date picker launches in the upper center of your screen. If you want to change the default position of the date picker, you can change the left and top percent values in the following line of the html action in the macro .

<body data-kmwindow="SCREEN(Main,Left,50%),SCREEN(Main,Top,20%),260,225">

Usage
Mouse clicking on a date in the date picker sets that as the start date for selected items.
Press escape key to close the date picker without selecting a start date.

Technical Nitpicks
I am using the inline version of the jquery date picker because it looks better for this purpose than the popover version. However, there does not appear to be a way to give the inline version focus, which means you cannot navigate the date picker with the keyboard. If anyone knows how to set focus to allow keyboard navigation of the inline date picker, I would like to know.

Credits:
@iNik for the Fancy Mac-Like HTML Prompts Library
@complexpoint for his code for getting KM variables from JavaScript and for his general scripting assistance on these forums
@jessegrosjean for his code that I also used for this macro

Download in this post over at the Keyboard Maestro forums.


#2

If you want to use the popover version of the date picker that enables keyboard navigation of the calendar, then replace this line in the HTML actoin:

<div id="datepicker"></div>

with this:

<p><input type="text" id="datepicker" autofocus></p>

Then you can use ⌘ + Arrow keys to navigate the calendar and press enter to select a date. You will need to press ESC twice to close the prompt without selecting a date.


#3

Phil,
Can this script be run without using Keyboard Maestro?


#4

No. It requires Keyboard Maestro.


#5

Keyboard Maestro is a very useful instrument, but if you want to experiment with using the standard NSDatePicker from JavaScript for Automation:

You could try something like this: startDatePicker.app.zip (51.8 KB)

Which needs to be saved from Script Editor as an .app (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

If you want to choose time you can drag the hands of the clock. It may be easier to hide the red seconds hand by editing from:

$.NSHourMinuteSecondsDatePickerElementFlag

to

$.NSHourMinuteDatePickerElementFlag

in the code below.

(function () {
    'use strict';

    // @start date picker for TaskPaper 3

    // INSTALLATION

    // 1. Needs to be saved as an **app** from Script Editor
    //       Save As > File Format > Application
    //       with: ''Stay open after run handler'' checked

    // 2. Place in ~/Library/Scripts/Application/TaskPaper
    //    e.g. from TaskPaper 3, click the Script icon in the OS X menu bar
    //    and choose: Open Script Folder > Open TaskPaper Scripts folder

    // 3. Run from TaskPaper 3, using the Script Menu

    // TASKPAPER CONTEXT


    function setDateAttribute(editor, options) {
        var strTag = options.tag,
            dteDate = options.when,
            item = editor.selection.startItem;

        if (strTag && dteDate && item) {
            item.setAttribute(
                'data-' + 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 -> maybe JS Date
    function alertResult(dctPickerView) {
        var oAlert = $.NSAlert.alloc.init;

        oAlert.setMessageText('Pick a date');
        oAlert.setInformativeText('to add @start tag in TaskPaper 3');
        oAlert.addButtonWithTitle('OK');
        oAlert.addButtonWithTitle('Cancel');
        oAlert.setAccessoryView(dctPickerView.view);

        return (oAlert.runModal === $.NSAlertSecondButtonReturn) ? (
            undefined
        ) : ObjC.unwrap(dctPickerView.picker.dateValue);
    }

    var a = Application.currentApplication(),
        sa = (a.includeStandardAdditions = true, a);

    a.activate();

    // JS date or undefined
    var baseDate = new Date();

    baseDate.setHours(0, 0, 0, 0);

    var maybeDate = alertResult(
        pickerView(
            datePicker(baseDate)
        )
    );

    if (maybeDate) {
        var tp3 = Application('TaskPaper'),
            ds = tp3.documents,
            d = ds.length ? ds[0] : (
                ds.push(tp3.Document()),
                ds[0]
            );

        d.evaluate({
            script: setDateAttribute.toString(),
            withOptions: {
                tag: 'start',
                when: maybeDate.toString()
            }
        });

        tp3.activate();
    }

    sa.quit();

})();

#6

@Phil
What do I have to change to the keyboard maestro script if I want a @start(YYYY-MM-DD Weekday) format input, say @start(2016-06-27 Mon) or @start(2016-07-10 Sun)

Please enlighten on this.