Search code examples
javascriptobjective-cmacosicalendarjavascript-automation

Comparisons / Rich queries in Javascript for Automation (JXA) "whose()"


If I wanted to look through my OS X Calendar (formerly "iCal") to find events whose summary was "Lunch", I could do something like this:

var eventsContainer = Application('Calendar').calendars[0].events
for (var i = 0; i < eventsContainer.length; i++) {
    var thisEvent = eventsContainer[i];
    if (thisEvent.summary() == 'Lunch') { doSomething() }
} 

even taking into account the fact that this only searches the first calendar, it's very, very, very slow, since each iCal event needs to be translated to a Javascript object to run. Here's a formulation that is substantially faster:

var foundEvents = Application('Calendar').calendars.events.whose({summary: 'Lunch'});

This works great for an exact match summary == 'Lunch'. But what about comparisons such as endDate: > new Date() or summary: /lunch/i ? Is it possible to pass native (ObjC) code into a whose() selector? Is there any documentation anywhere for whose() that would help?


Solution

  • The relevant documentation turned out to be in the Release Notes for Interapplication Communication for OS X 10.10: https://developer.apple.com/library/mac/releasenotes/InterapplicationCommunication/RN-JavaScriptForAutomation/Articles/OSX10-10.html

    The right-hand side of the object in the whose() argument can take another one-element object where the left side gives an operator and the right side gives a value. For instance:

    .calendars.events.whose({summary: {_beginsWith: 'Lunch'}});
    

    or in my case, to find events starting today:

        var startOfDay = new Date();
        startOfDay.setHours(0);
        startOfDay.setMinutes(0);
        startOfDay.setSeconds(0);
        startOfDay.setMilliseconds(0);
        var endOfDay = new Date();
        endOfDay.setHours(23);
        endOfDay.setMinutes(59);
        endOfDay.setSeconds(59);
        endOfDay.setMilliseconds(999);
    
        var events = Application('Calendar').calendars.events.whose({
            _and: [
                { startDate: { _greaterThan: startOfDay }},
                { startDate: { _lessThan: endOfDay }}
            ]
        });
        var convertedEvents = events();
        for (var cal of convertedEvents) {
            for (var ev of cal) { 
                console.log(ev.summary());
            }
        }