A collection of Javascript for Automation (JXA) examples for reference and learning. All the code in this repository was run and tested on macOS 10.15.4 Catalina.
JXA is the Javascript implementation of AppleScript and it allows to script Apple's standard applications, such as Calendar, Reminders, and Music. It can also be used to control the UI. There aren't any recent books or tutorials on JXA so I hope this collection will make your learning a little easier. Other resources are linked at the bottom of the page.
Task Boxes Basic uses various snippets from the examples below to create a basic productivty tool.
I have not done enough testing determine whether the following are bugs or in JXA or I am using it incorrectly
Jump to Index.
Open in Editor: Jump to Index | Top
    var app = Application.currentApplication()
    var Calendar = Application("Calendar")
    var projectCalendars = Calendar.calendars.whose({name: "Project Calendar"})
    var projectCalendar = projectCalendars[0]
    var events = projectCalendar.events.whose({summary: "Important Meeting!"})
    var event = events[0]
    var attendee = Calendar.Attendee({email: "example@apple.com"})
    event.attendees.push(attendee)
    Calendar.reloadCalendars();
  Open in Editor: Jump to Index | Top
    Mail = Application('Mail')
    Mail.name()
    Mail.name
    Mail.outgoingMessages[0]
    //Mail.accounts[0].mailboxes[8].messages[0]()
    //Mail.accounts[0].mailboxes[8]()
    Mail.activate() //bring Mail to foreground
    Mail.accounts['iCloud'].checkForNewMail()
    // show latest message in inbox
    var latestMsg = Mail.inbox.messages[0]
    Mail.open(latestMsg);
  Open in Editor: Jump to Index | Top
    // Example Code on WWDC talk
    Keynote = Application("Keynote")
    Keynote.activate() //foreground
    //document class, make it a method, and add make verb at the end.
    //newDoc = Keynote.Document().make()
    //can create with properties set as record
    doc = Keynote.Document({
        documentTheme:Keynote.themes["Artisan"],
        width: 1024,
        height: 768,
        autoPlay: true,
        autoLoop: true,
        autoRestart:true,
        maximumIdleDuration:3
        }).make();
  Open in Editor: Jump to Index | Top
  //Trying to implement Apple Example Code, but it doesn't work out of box.
    Mail = Application('Mail')
    TextEdit = Application('TextEdit')
    Mail.name
    para = TextEdit.Paragraph({}, 'Some text')
    TextEdit.documents[0].paragraphs.push(para);
  Open in Editor: Jump to Index | Top
    // Include Apple's UI elements
    var app = Application.currentApplication()
    app.includeStandardAdditions = true
    var response = app.displayDialog("What's your name?", {
        defaultAnswer: "",
        withIcon: "note",
        buttons: ["Cancel", "Continue"],
        defaultButton: "Continue"
    })
    // Result: {"buttonReturned":"Continue", "textReturned":"Jen"}
    app.displayDialog("Hello, " + (response.textReturned) + ".");
  Open in Editor: Jump to Index | Top
    var app = Application.currentApplication()
    app.includeStandardAdditions = true
    function writeTextToFile(text, file, overwriteExistingContent) {
        try {
            // Convert the file to a string
            var fileString = file.toString()
            // Open the file for writing
            var openedFile = app.openForAccess(Path(fileString), { writePermission: true })
            // Clear the file if content should be overwritten
            if (overwriteExistingContent) {
                app.setEof(openedFile, { to: 0 })
            }
            // Write the new content to the file
            app.write(text, { to: openedFile, startingAt: app.getEof(openedFile) })
            // Close the file
            app.closeAccess(openedFile)
            // Return a boolean indicating that writing was successful
            return true
        }
      catch(error){
            try {
                // Close the file
                app.closeAccess(file)
            }
            catch(error) {
                // Report the error is closing failed
                console.log(`Couldn't close file: ${error}`)
            }
            // Return a boolean indicating that writing was successful
            return false
        }
    };
  Open in Editor: Jump to Index | Top
    // Loads Apple's UI elements and others.
    app = Application.currentApplication()
    app.includeStandardAdditions = true
    app.say('Greetings')
    dialogResult = app.displayDialog('Please enter your name', {
        withTitle: 'Greetings',
        defaultAnswer: '',
        buttons: ["Goodbye", "Hello"],
        defaultButton: 2
    })
    if (dialogResult.buttonReturned == "Hello") {
        app.say('Hello ' + dialogResult.textReturned)
        } else {
        app.say('Goodbye' + dialogResult.textReturned);
  Open in Editor: Jump to Index | Top
    // Creates a Calendar
    var Calendar = Application("Calendar")
    var calendarName = "Demo"
    var calendarDescription = "Demo Calendar"
    var newCalendar = Calendar.Calendar({name: calendarName, description: calendarDescription}).make()
    // Result: Application("Calendar").calendars.at(3);
  Open in Editor: Jump to Index | Top
    var app = Application.currentApplication()
    app.includeStandardAdditions = true
    var Calendar = Application("Calendar")
    var eventStart = app.currentDate()
    eventStart = eventStart
    eventStart.setDate(eventStart.getDate() + 1)
    eventStart.setHours(15)
    eventStart.setMinutes(0)
    eventStart.setSeconds(0)
    var eventEnd = new Date(eventStart.getTime())
    eventEnd.setHours(16)
    var projectCalendars = Calendar.calendars.whose({name: "DEMO"})
    //Use the first result that matches name search
    var projectCalendar = projectCalendars[0]
    var event = Calendar.Event({summary: "Important Meeting!", startDate: eventStart, endDate: eventEnd})
    projectCalendar.events.push(event);
  Open in Editor: Jump to Index | Top
    ////Requires "Demo" calendar with events
    var Calendar = Application("Calendar")
    var projectCalendars = Calendar.calendars.whose({name: "Demo"})
    var projectCalendar = projectCalendars[0]
    var events = projectCalendar.events.whose({summary: "Important Meeting!"})
    var event = events[0]
    event.show();
  Open in Editor: Jump to Index | Top
  var app = Application.currentApplication()
    app.includeStandardAdditions = true
    var file = app.chooseFile({
        ofType: "txt",
        withPrompt: "Please select a text file to read:"
    })
    readFile(file);
  Open in Editor: Jump to Index | Top
  //How to count number of events in a library?
    // All calendars
    Application('Calendar').calendars.events.length
    //First Calendar in your List
    Application('Calendar').calendars[0].events.length;
  Open in Editor: Jump to Index | Top
    // Sometimes on first run it fails with invalid index, but unable to repeat
    var app = Application.currentApplication()
    app.includeStandardAdditions = true
    ////Requires "Demo" calendar with events
    //Create new event starting now and ending in one hour
    //Set Calendar to be the Application Calendar
    var Calendar = Application('Calendar')
    //Set name of Calendar you are using
    var calendarUsed = "Demo";
    //use Demo as the used Calendar by searching for it.
    var projectCalendars = Calendar.calendars.whose({name: calendarUsed});
    //Use the first result that matches name search
    var projectCalendar = projectCalendars[0];
    //You need the time now as an object
    var timeNow = new Date();
    //inAnHour as well
    var inAnHour = new Date();
    //Create Date object one hour ago for search criteria
    inAnHour.setHours(timeNow.getHours()+1);
    //Create new event with values from pervious event and end time of timeNow
    newEvent=Calendar.Event({summary: "Demo Event", startDate: timeNow, endDate: inAnHour});
    //Fails with invalid index on first run.
    projectCalendar.events.push(newEvent);
  Open in Editor: Jump to Index | Top
    ////Requires "Demo" calendar with events
    //Creates new Event with same startDate as current event and endDate as Now
    // Deletes the event it replaces
    // Add standard library for dialog boxes
    var app = Application.currentApplication()
    app.includeStandardAdditions = true
    //Set Calendar to be the Application Calendar
    var Calendar = Application('Calendar')
    //Set name of Calendar you are using
    var calendarUsed = "Demo";
    //use Demo as the used Calendar by searching for it.
    var projectCalendars = Calendar.calendars.whose({name: calendarUsed})
    //Use the first result that matches name search
    var projectCalendar = projectCalendars[0]
    var testEvents = projectCalendar.events.whose({ summary: "TEST"});
    //set recentEvent to be the first in the results of search.
    //TODO deal with more than one search result
    var testEvent = testEvents[0]
    eventId = testEvent.id()
    //Delete the event with the old end time.
    Calendar.delete(projectCalendar.events.byId(eventId));
  Open in Editor: Jump to Index | Top
    //Event elements can be added, properties cannot be changed
    ////Requires "Demo" calendar with events
    //Set Calendar to be the Application Calendar
    var Calendar = Application('Calendar')
    //use Demo as the used Calendar by searching for it.
    var projectCalendars = Calendar.calendars.whose({name: "Demo"})
    //Use the first result that matches name search
    var projectCalendar = projectCalendars[0]
    //new keyword makes Date object
    var timeNow = new Date()
    //Creation of a new event with same start and end time as the pulled event
    newEvent=Calendar.Event({summary: "TEST", startDate: timeNow, endDate: timeNow})
    // Push the event to the calendar
    projectCalendar.events.push(newEvent)
    //Choose which event I will edit. In this case first one. Could also be .last or array value []
    var event = projectCalendar.events.last()
    // attendee is made as a function. Class Event contains element attendeess, which is why I can add(push) attendees.
    var attendee = Calendar.Attendee({email: "example@apple.com"})
    event.attendees.push(attendee)
    event.attendees[0]();
    event.startDate();
    //new Event(myEvent)
    //projectCalendar.events.length;
  Open in Editor: Jump to Index | Top
    ////Requires "Demo" calendar with events
    var Calendar = Application('Calendar')
    // Create demoDays object, which is an array of events where summaries start with Blog
    var demoDays = Application('Calendar').calendars[12].events.whose({ summary: { _beginsWith: 'Demo' } })()
    //Result: [Application("Calendar").calendars.byId("3E7ADB20-0113-4FD7-B04B-2AA470B83380").events.byId("5D241B42-D0EF-4312-B99F-E1E38A2E09A0"), Application("Calendar").calendars.byId("3E7ADB20-0113-4FD7-B04B-2AA470B83380").events.byId("C6E3B842-AAB3-4E95-8106-918E1FCB1C4C")]
    //Number of events starting with Blog in the Demo Calendar
    demoDays.length
    //Result: 2
    // Summary of first event returned
    demoDays[0].summary()
    // Result: "Blog Apple launchd"
    // Date event started
    demoDays[0].startDate()
    // Result: Fri Jul 19 2019 08:50:00 GMT+0200 (CEST)
    // it is an "object"
    //Date in milliseconds
    Date.now()
    //Result: 1584451353422
    var timeNow = new Date()
    //new keyword makes Date object
    var oneHour = 60 * 60 * 1000;
    //Create Date object that is one hour ago timeNow.
    var anHourAgo = new Date(new Date() - oneHour);
    //https://stackoverflow.com/questions/38023712/comparisons-rich-queries-in-javascript-for-automation-jxa-whose
    var recentDemoEvents = Application('Calendar').calendars[12].events.whose({
        _and: [
            { startDate: { _greaterThan: anHourAgo }},
            { startDate: { _lessThan: timeNow}}
        ]
    });
    //Array of events that started in the past hour.
    recentDemoEvents();
    //Single event's summary that started in past hour from the Demo Calendar
    recentDemoEvents[0]().summary();
  Open in Editor: Jump to Index | Top
    //Find Event in past hour in specific calendar.
    //the events are not in order in the Calendar.events array, therefore you need specify time frames
    var Calendar = Application('Calendar')
    Calendar.reloadCalendars();
    //Use the Demo calendar
    usedCalendar=Application('Calendar').calendars[12]
    var timeNow = new Date()
    //new keyword makes Date object
    var oneHour = 60 * 60 * 1000;
    //Create Date object that is one hour ago timeNow.
    var anHourAgo = new Date(new Date() - oneHour);
    //https://stackoverflow.com/questions/38023712/comparisons-rich-queries-in-javascript-for-automation-jxa-whose
    var recentDemoEvents = Application('Calendar').calendars[12].events.whose({
        _and: [
            { startDate: { _greaterThan: anHourAgo }},
            { startDate: { _lessThan: timeNow}}
        ]
    });
    //Array of event functions that started in the past hour. The event function has properties that are objects.
    recentDemoEvents();
    //Single event that started in past hour
    typeof(recentDemoEvents[0]().endDate());
    recentDemoEvents[0]()
    //Result:Application("Calendar").calendars.byId("3E7ADB20-0113-4FD7-B04B-2AA470B83380").events.byId("A9154DCD-9E76-4383-8ABC-5062D578EE5E")
    var event = recentDemoEvents[0]();
    //You can create new objects by calling class constructors as functions
    event;
  Open in Editor: Jump to Index | Top
  //Read the number of Events in a given calendar.
    Application('Calendar').calendars[0].events.length;
  Open in Editor: Jump to Index | Top
    //Looking at the Alarm poperty on an event
    // Requires an Automator calendar with even and alarm property.
    // Working with Application Calendar
    var Calendar = Application("Calendar")
    // Use specific calendar matching name
    var projectCalendars = Calendar.calendars.whose({name: "Automator"})
    //Use the first calendar that matches the name
    var projectCalendar = projectCalendars[0]()
    // Gets information for the last created element in calendar
    var eventinfo = projectCalendar.events.last()
    // Have a single object to return with all the information
    eventinfo.openFileAlarms();
    eventinfo.properties();
    projectCalendar.keys();
  Open in Editor: Jump to Index | Top
    //https://leancrew.com/all-this/
    //If "Example Event" and "Demo" calendar do not exist script fails
    var now = new(Date)
    var Calendar = Application("Calendar")
    var homeCal = Calendar.calendars.whose({name: "Demo"})[0]
    var swHomeEvents = homeCal.events.whose({
      _and: [
        {startDate: {_greaterThan: now}},
        {summary: {_beginsWith: "Example Event"}}
      ]
    })
    var swEvents = swHomeEvents()
    swEvents;
  Open in Editor: Jump to Index | Top
    //Requires "Demo" calendar with events
    // Working with Application Calendar
    var Calendar = Application("Calendar")
    // Use specific calendar matching name
    var projectCalendars = Calendar.calendars.whose({name: "Demo"})
    //Use the first calendar that matches the name
    var projectCalendar = projectCalendars[0]()
    // Gets information for the last created element in calendar
    var eventinfo = projectCalendar.events.last()
    eventinfo.properties();
  Open in Editor: Jump to Index | Top
    //Shows Example Event in Calendar Demo.
    //If "Example Event" and "Demo" calendar do not exist script fails
    var Calendar = Application("Calendar")
    //Returns all Calendars with matching name
    var projectCalendars = Calendar.calendars.whose({name: "Demo"})
    //Only use the first one that matches name
    var projectCalendar = projectCalendars[0]
    //Same logic as with Calendar, returns all, you only want the first one.
    var events = projectCalendar.events.whose({summary: "Example Event"})
    var event = events[0]
    event.show();
  Open in Editor: Jump to Index | Top
    //Set Calendar to be the Application Calendar
    var Calendar = Application('Calendar')
    //Set name of Calendar you are using
    var calendarUsed = "Demo"
    //use Demo as the used Calendar by searching for it.
    var projectCalendars = Calendar.calendars.whose({name: calendarUsed})
    //Use the first result that matches name search
    var projectCalendar = projectCalendars[0]
    events = projectCalendar.events()
    // This sorts all events so it may take a while
    events.sort(function (a,b){
        if (a.startDate() > b.startDate()) return -1;
        if (a.startDate() < b.startDate()) return 1;
        return 0
        });
    // Script Editor only outputs one value, so an array to show that start dates are in order
    var holderArray= []
    for (let i=0; i < events.length; i++){
        holderArray.push(events[i]);
        }
    // Most Recent event is now first as sort is decending
    events[0].startDate();
  Open in Editor: Jump to Index | Top
    // Unlike elements which can be added, properties cannot be changed, but new events must be created with new property and the previous event deleted.
    // is this correct? https://stackoverflow.com/questions/9454863/updating-javascript-object-property/48209957
    // This script creates an example event and then based on that event creates a new event with a new description property.
    // Add standard library for dialog boxes
    var app = Application.currentApplication();
    app.includeStandardAdditions = true;
    //Set Calendar to be the Application Calendar
    var Calendar = Application('Calendar');
    //Set name of Calendar you are using
    var calendarUsed = "Demo";
    //use Demo as the used Calendar by searching for it.
    var projectCalendars = Calendar.calendars.whose({name: calendarUsed});
    //Use the first result that matches name search
    var projectCalendar = projectCalendars[0];
    //You need the time now as an object
    var timeNow = new Date();
    //inAnHour as well
    var inAnHour = new Date();
    //Create Date object one hour ago for search criteria
    inAnHour.setHours(timeNow.getHours()+1);
    //Create new event with values from pervious event and end time of timeNow
    newEvent=Calendar.Event({summary: "Demo Event", startDate: timeNow, endDate: inAnHour});
    // Push the event to the calendar
    projectCalendar.events.push(newEvent);
    // Get ID of last created event to be used to delete the event without description
    var eventWithoutDescriptionId = projectCalendar.events.last.id()
    var descriptionText = "This event was created with a description";
    //Create new event with same valuses as it replaces and new descritption
    eventWithDescription=Calendar.Event({summary: "Demo Event" , startDate: timeNow, endDate: inAnHour, description: descriptionText})
    // push the event to the calendar
    projectCalendar.events.push(eventWithDescription)
    //Delete the event without description.
    try {
    Calendar.delete(projectCalendar.events.byId(eventWithoutDescriptionId))
    }
    catch(err){
        var errorMsg = app.displayDialog("Error", {
        buttons: ["Cancel", "Continue"],
        defaultButton: "Continue"
        })
    };
  Open in Editor: Jump to Index | Top
  // Provids the full path to current files as a Path object use toString() if you want a string
    var app = Application.currentApplication();
    app.includeStandardAdditions = true;
    //
    thePath = app.pathTo(this);
  Open in Editor: Jump to Index | Top
    // Looking at the properties of diskItems
    var Finder = Application("Finder");
    var SystemEvents = Application("System Events");
    var sourcePathStr = "~/Demo/sampleDir"
    var expandedSourcePathStr = $(sourcePathStr).stringByStandardizingPath.js
    //Result: "/Users/adkj/Demo/sampleDir"
    // function sourceFolder
    var sourceFolder = SystemEvents.aliases.byName(expandedSourcePathStr);
    //Result: Application("System Events").aliases.byName("/Users/adkj/Demo/sampleDir")
    //function container
    var container = sourceFolder.container();
    //Result: Application("System Events").folders.byName("Macintosh HD:Users:adkj:Demo")
    // String containerPath
    var containerPath = container.path();
    //Result: "Macintosh HD:Users:adkj:Demo"
    // Create an array of items functions to be processed
    var items = SystemEvents.aliases.byName(sourcePathStr).diskItems;
    items[1]()
    //Result: Path("/Users/adkj/Demo/sampleDir/samplefile.rtf")
    items[1].class()
    //Result: "file"
    items[1].name()
    //Result: "samplefile.rtf"
    items[0].path()
    //Result: "Macintosh HD:Users:adkj:Demo:sampleDir:.DS_Store";
  Open in Editor: Jump to Index | Top
    var app = Application.currentApplication();
    app.includeStandardAdditions = true;
    var Finder = Application("Finder");
    var SystemEvents = Application("System Events");
    // Ask a folder to process
    var sourcePathStr = "~/Demo/sampleDir"
    //Expands ~ to be full user path
    var expandedSourcePathStr = $(sourcePathStr).stringByStandardizingPath.js
    // Allows for running the script multiple times
    var duplicateAvoider = Date.now().toString()
    // function sourceFolder
    var sourceFolder = SystemEvents.aliases.byName(expandedSourcePathStr);
    //Result: Application("System Events").aliases.byName("/Users/adkj/Demo/sampleDir")
    // Create the destination folder
    //function container
    var container = sourceFolder.container();
    //Result: Application("System Events").folders.byName("Macintosh HD:Users:adkj:Demo")
    // String containerPath
    var containerPath = container.path();
    //Result: "Macintosh HD:Users:adkj:Demo"
    // .make is function/method from Standard Suite
    var destinationFolder = Finder.make(
        {
             new: "folder",
            at: containerPath,
            withProperties:
            {
                name: sourceFolder.name() + " - New"
            }
        }
    );
    // Create an array of items functions to be processed
    var items = SystemEvents.aliases.byName(sourcePathStr).diskItems;
    items[0]()
    //Result: Path("/Users/adkj/Demo/sampleDir/samplefile.rtf")
    items[0].class()
    //Result: "file"
    items[0].name()
    //Result: "samplefile.rtf"
    items[1].path()
    //Result: "Macintosh HD:Users:adkj:Demo:sampleDir:.DS_Store"
    //Finder.move("Macintosh HD:Users:adkj:Demo:sampleDir:samplefile.rtf", { to: "Macintosh HD:Users:adkj:Demo:sampleDir - New" });
  Open in Editor: Jump to Index | Top
    // Script to Open a Finder Window with a folder and bring to the front
    // Requires ~/Demo
    var Finder = Application('Finder');
    var demoDir = "~/Demo"
    //Resolves the ~ to be the home directory path
    var fullDemoDir = $(demoDir).stringByStandardizingPath.js
    //Opens Finder window containing ~/Demo
    // Brings Path object into view
    Finder.reveal(Path(fullDemoDir));
    //Brings finder to front
    Finder.activate();
  Open in Editor: Jump to Index | Top
  // Make a new directory
    var app = Application.currentApplication();
    app.includeStandardAdditions = true;
    var Finder = Application("Finder");
    Finder.make(
        {
             new: "folder",
            //Requires password to create folder here
            at: "Macintosh HD:Users",
            withProperties:
            {
                name: "Made by JXA"
            }
        }
    );
  Open in Editor: Jump to Index | Top
    // Move files from one directory to another
    // based on https://stackoverflow.com/questions/31710494/moving-created-files-with-jxa
  
    'use strict';
    var app = Application.currentApplication()
    app.includeStandardAdditions = true
    var Finder = Application('Finder');
    // Create a Path object
    var sourceFile = Path("/User/adkj/Demo/sample.rtf");
    var destintationFile = "/Users/adkj/Demo2/sample" ;
    var destFolder=Path("/Users/adkj/Demo2");
    var strPathSource = $(sourceFile).toString();
    var strPathDest = $(destFolder).toString();
    Finder.move(strPathSource, { to:strPathDest, replacing: true});
  Open in Editor: Jump to Index | Top
  //.calendaridentifier()
    var calTaskBox = Application('Calendar').calendars[9]
    calTaskBox.calendarIdentifier();
  Open in Editor: Jump to Index | Top
  // Example of sorting by the Date Object
    array = [new Date(), new Date(100001), new Date(100100800000), new Date(1989, 9, 11, 8, 46, 00, 0)]
    var sortedArray = array.sort((a, b) => a.valueOf() - b.valueOf());
  Open in Editor: Jump to Index | Top
    //array of object's enumerable string properties
    Object.keys(Application('Calendar').calendars[12].events)
    //Result : ["0", "1", "2", "3", "4", "5"]
    //Get name of calendar 12
    Application('Calendar').calendars[12].name()
    //Result: "Demo"
    //Summary is the name of the event
    Application('Calendar').calendars[12].events.summary()
    //Result: ["Demo Project 1 - makeing video", "Cheese Making - Researching soy cheddar", "Blog Apple launchd", "Blog research ", "Automating TimeMachine Backups", "Chess"];
  Open in Editor: Jump to Index | Top
    (() => {
        'use strict';
        // evalAS :: String -> IO String
        const evalAS = s => {
            const
                a = Application.currentApplication(),
                sa = (a.includeStandardAdditions = true, a);
            return sa.doShellScript(
                ['osascript -l AppleScript <<OSA_END 2>/dev/null']
                .concat([s])
                .concat('OSA_END')
                .join('\n')
            );
        };
        return evalAS('say "hello world"');
    })();
  Open in Editor: Jump to Index | Top
  // Quit an open Application
    var pages = Application('Pages');
    pages.quit();
  Open in Editor: Jump to Index | Top
    // I found this somewhere, reference it when found again
    Application("Mission Control").launch()
    var proc = Application("System Events").processes['Dock']
    var group = proc.groups[0].groups[0].groups[1]
    var bs = group.buttons.whose({ description: "add desktop"})
    Application("System Events").click(bs[0])
    delay(0.5)
    var li = group.lists[0]
    Application("System Events").click(li.buttons[li.buttons.length - 1])
    delay(0.5)
    Application("Calendar").activate();
    Application("Reminders").activate();
  Open in Editor: Jump to Index | Top
  // Toggle between Playing and pausing current song
    Music = Application('Music');
    Music.playpause();
  Open in Editor: Jump to Index | Top
    // Maybe from JXA cookbook
    (function () {
        'use strict';
        // GENERIC FUNCTIONS ------------------------------------------------------
        // doesFileExist :: String -> Bool
        function doesFileExist(strPath) {
            var error = $();
            return $.NSFileManager.defaultManager
                .attributesOfItemAtPathError($(strPath)
                    .stringByStandardizingPath, error), error.code === undefined;
        };
        // lines :: String -> [String]
        function lines(s) {
            return s.split(/[\r\n]/);
        };
        // readFile :: FilePath -> maybe String
        function readFile(strPath) {
            var error = $(),
                str = ObjC.unwrap(
                    $.NSString.stringWithContentsOfFileEncodingError($(strPath)
                        .stringByStandardizingPath, $.NSUTF8StringEncoding, error)
                ),
                blnValid = typeof error.code !== 'string';
            return {
                nothing: !blnValid,
                just: blnValid ? str : undefined,
                error: blnValid ? '' : error.code
            };
        };
        // show :: a -> String
        function show(x) {
            return JSON.stringify(x, null, 2);
        };
        // TEST -------------------------------------------------------------------
        var strPath = '~/DeskTop/random.txt';
        return doesFileExist(strPath) ? function () {
            var dctMaybe = readFile(strPath);
            return dctMaybe.nothing ? dctMaybe.error : show(lines(dctMaybe.just));
        }() : 'File not found:\n\t' + strPath;
    })();
  Open in Editor: Jump to Index | Top
    // Uses Objective C to read file names in a given folder.
    // Copied from the JXA/Cookbook https://github.com/JXA-Cookbook/JXA-Cookbook
        // listDirectory :: FilePath -> [FilePath]
        function listDirectory(strPath) {
            fm = fm || $.NSFileManager.defaultManager;
            return ObjC.unwrap(
                    fm.contentsOfDirectoryAtPathError($(strPath)
                        .stringByExpandingTildeInPath, null))
                .map(ObjC.unwrap);
        }
        var fm = $.NSFileManager.defaultManager;
        listDirectory('~/Documents');
  Open in Editor: Jump to Index | Top
  // Create new Reminders List
    var Reminders = Application("Reminders");
    //Create new List object. Other properties are color, container, and emblem.
    var newReminderList = Reminders.List({name: "TEST"});
    Reminders.lists.push(newReminderList);
  Open in Editor: Jump to Index | Top
  // Extracting Reminder list properties
    var Reminders = Application("Reminders");
  Open in Editor: Jump to Index | Top
    var Reminders = Application("Reminders")
    // Open reminders on the screen to see selected reminder
    Reminders.activate()
    //Reminders.lists.byName('List 1').show()
    // You need to find a correct Id.
    Application("Reminders").reminders.byId("x-apple-reminder://D20AFA87-6606-48D9-A4D0-758B1A8F07DB").show();
  Open in Editor: Jump to Index | Top
    // Create new Reminder
    // from https://www.richardhyde.net/2019/04/16/New-Reminder-Using-JXA.html
    var reminders = Application("Reminders");
    var newReminder = reminders.Reminder({ name: "Title for reminder", body: "Notes for the reminder"});
    reminders.lists.byName("List Name").reminders.push(newReminder);
  Open in Editor: Jump to Index | Top
    // Include Apple UI library
    var app = Application.currentApplication()
    app.includeStandardAdditions = true
    var errorMsg = app.displayDialog("Error", {
    buttons: ["Cancel", "Continue"],
    // icon alternatives are stop, note, caution
    withIcon: "stop",
    defaultButton: "Continue"
    });
  Open in Editor: Jump to Index | Top
  var app = Application.currentApplication()
    app.includeStandardAdditions = true
    var sourceFilePath = Path("/User/adkj/Demo/sample.rtf");
    var sourceFile = "/User/adkj/Demo/sample.rtf"
    app.exists(sourceFile)
    // Result: true;
  Open in Editor: Jump to Index | Top
  var Reminders = Application('Reminders');
    Reminders.activate();
    delay(4)
    Reminders.quit();
  Open in Editor: Jump to Index | Top
  var app = Application.currentApplication();
    app.includeStandardAdditions = true;
    // Starts screensaver
    app.doShellScript('/System/Library/CoreServices/ScreenSaverEngine.app/Contents/MacOS/ScreenSaverEngine -background &');
  Open in Editor: Jump to Index | Top
  //Spotlight to use Deep link, in this case Red Hot Timer
    app = Application.currentApplication();
    app.includeStandardAdditions = true;
    app.openLocation("timer://30m");
  Open in Editor: Jump to Index | Top
    // To use UI elements
    var app = Application.currentApplication()
    app.includeStandardAdditions = true
    // Array of things to choose from
    var choices = ["Red", "Orange", "Yellow", "Green", "Blue", "Indigo", "Violet"]
    var chooseItem = app.chooseFromList(choices, {
    defaultAnswer: "Red",
        withPrompt: "Choose a color"
    }
    )
    // Returns array of choices
    chooseItem
    //Stringify the choice
    choosenItem = chooseItem[0];
  Open in Editor: Jump to Index | Top
  // Get user information
    var SystemEvents = Application('System Events')
    SystemEvents.currentUser.homeDirectory()
    SystemEvents.currentUser();
    // Get user information
    var SystemEvents = Application('System Events')
    SystemEvents.currentUser.homeDirectory()
    SystemEvents.currentUser();