Learn JavaScript with me

Seems about time that I did this.

As an OmniFocus user and a Drafts fan it’s about time I learned JavaScript. I’ve been meaning to learn some basic programming skills for a while and now seems a great opportunity because I get the feeling I could make a lot of huge workflow gains if I had a JS under my belt.

Learning is easier if you do it in a group, especially if that group is mixed ability, including some who can deepen their learning by helping others. So this is me raising a flag to see if anyone fancies doing this with me.

I’m guessing we could use this forum as a place to collaborate on our learning. Perhaps a slack group might be easier? Im open to ideas.

So, who’s up for it?

10 Likes

You know me, I’m in! I’m probably mid-level, as I’m a developer by day. But I’m more than willing to help others :slight_smile:

2 Likes

I’m absolutely certain that you’re more than “mid level” but I’m happy to allow you to be modest. :slight_smile:

I’m thinking a good size for a learning group is about five or so. What do you think?

1 Like

Thinking about this, would a JSDrafts library make sense?

I believe programmers often use chunks of code that others have made. Does it make sense for the Drafts community to have something like this?

For instance, someone gave me a script that searches Drafts by tag and then grabs email addresses so I can do mailing to groups within Drafts. Presumably that same script could be used to grab other types of data.

2 Likes

Great idea i‘m a Software developer but didnt really use Java Script Outsider of drafts :slight_smile:

Count me in!!! (to learn that is)

Ok! Good. We should probably choose a starting point.

Maybe here?

https://www.w3schools.com/jsref/jsref_obj_array.asp

2 Likes

The name is a little misleading – suggests some kind of association with the standards body, but it’s really a commercial setup and the content is not consistently reliable.

The Mozilla pages are of better quality, and more reliable:

The Array methods are a very good starting point though …

(map, filter and reduce or reduceRight, in particular)

1 Like

If y’all are interested in learning by example, the 1Writer action directory is a good place to start. See Adapting 1Writer actions - source for JavaScript recipes

I’ve adapted four of the scripts so far to Drafts and it’s generally pretty easy - you just need to work out the differences between the app-provided objects, but quite a lot of the code is easily reused. And even if not, it’s helpful to look at how others have solved a ton of text manipulation tasks.

1 Like

The most useful thing for me would be fore people who have created JS actions for Drafts to share versions of their code that are heavily documented so we can understand what they are doing and how they can be adapted. It would also be good to better understand some of the most common error messages and what they mean.

I would love to learn even the basics.

I can do that; and help pick apart other ones too.

Of course, reading Greg’s scripting documentation first would be necessary, but then you would like have specific questions which I can help out with I hope!

2 Likes

So I’m seeing two good starting points here:

  1. We should all read and discuss the basics of JS.

  2. We should grab some examples of readymade scripts and debug/later/break and fix them.

Probably best to agree on this. How does it sound?

3 Likes

A sample comparison of Applescript and Javascript (ES6, as in Drafts 5, and in macOS Sierra onwards):

range

It can be useful to have a function which returns a rising list of integers, in exchange for start and end integers. In Python you might write range(5,15), in Haskell [5..15], and in AppleScript perhaps something like:

-- range :: Int -> Int -> [Int]
on range(m, n)
    if m ≤ n then
        set lst to {}
        repeat with i from m to n
            set end of lst to i
        end repeat
        return lst
    else
        return {}
    end if
end range

-- TEST --------------------------------

range(0, 9)
--> {0, 1, 2, 3, 4, 5, 6, 7, 8, 9}

range(5, 15)
--> {5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15}

Here are three ways of defining a reusable function like this for Javascript (range, range2, range3)

(() => {
    'use strict';

    // Declaring a function with 'function'
    // (Slightly more bells and whistles -
    //  1. Makes a special array-like 'arguments' value available
    //       in the body of the function
    //  2. locally redefines the special 'this' value
    //  3. lifts the function definition to the top of the code
    //     in terms of evaluation sequence,
    //       so that it is available even to other functions defined before it.

    // range :: Int -> Int -> [Int]
    function range(m, n) {
        if (m <= n) {
            let lst = [];
            for (let i = m; i <= n; i++) {
                lst.push(i)
            }
            return lst;
        } else {
            return [];
        }
    }

    // Declaring a function as a constant value, using the => 'arrow' pattern.
    // a bit more light-weight:
    //        - No array-like 'arguments' value created
    //        - No local redefinition of 'this'
    //        - Not lifted to the top of the file during evaluation, so available only
    //          from the point of definition onwards.

    // range2 :: Int -> Int -> [Int]
    const range2 = (m, n) => {
        if (m <= n) {
            let
                lst = [],
                i = m;
            while (i <= n) {
                lst.push(i);
                i++
            }
            return lst;
        } else {
            return [];
        };
    };

    // Using:
    //  1. Array.from (See the Mozilla page),
    //  2. the conditional ternary operator (which returns a value),
    //        rather than an if ... else statement, which returns no value
    //  3. and no variable declarations, so we need no braces or 'return'.

    // range3 :: Int -> Int -> [Int]
    const range3 = (m, n) =>
        m <= n ? (
            Array.from({
                length: (n - m) + 1
            }, (x, i) => m + i)
        ) : [];


    return {
        range: range(5, 15), //--> [5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15]
        range2: range2(5, 15), //--> [5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15]
        range3: range3(5, 15) //--> [5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15]
    };

})();

For a fourth version (the one that I happen to use myself, under a slightly less elegant name than range):

// enumFromToInt :: Int -> Int -> [Int]
const enumFromToInt = (m, n) =>
    n >= m ? (
        iterateUntil(x => x >= n, x => 1 + x, m)
    ) : [];

It uses another generic reusable function – iterateUntil – and we might test it by writing something like:

(() => {
    'use strict';

    // TEST -----------------------------------------------

    // main :: () -> [Int]
    const main = () =>
        enumFromToInt(5, 15);

        // --> [5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15]
        

    // GENERIC REUSABLES ----------------------------------

    // enumFromToInt :: Int -> Int -> [Int]
    const enumFromToInt = (m, n) =>
        n >= m ? (
            iterateUntil(x => x >= n, x => 1 + x, m)
        ) : [];

    // iterateUntil :: (a -> Bool) -> (a -> a) -> a -> [a]
    const iterateUntil = (p, f, x) => {
        let vs = [x],
            h = x;
        while (!p(h))(h = f(h), vs.push(h));
        return vs;
    };

    // MAIN -----------------------------------------------
    return main();

})();

I may well watch along, though doubt I’ll have much of value to contribute, as the last time I did any programming was about 18 years ago, in C… I’m sure I ought to learn some JavaScript, though.

I’m sure we’ll all learn together. But we need to begin with something really simple.

Anyone fancy offering us a basic task? Something we can build together to use in Drafts?

One traditional route might be to start with a ‘hello world’ , and then a series of incrementally more complex values (sheets, interactions, etc)

So perhaps starting with:

  1. A new draft containing a phrase like ‘hello world’
  2. A new draft containing seven lines of text - each the name of a day of the week, starting with today’s, or
  3. A new draft containing 12 rows - each the name of a month, starting with this month’s.

?

1 Like

I like this idea a lot. OK.

Shall we agree maybe five items and set a date to review?

1 Like

Yes, I was going to suggest trying to complete a given task. Here’s the one I thought would be good:

  • create an action that prompts the user to select a markdown heading level, heading text, then prepend the result with today’s date & insert into the current draft.

That’ll develop the skills for 80% of use scenarios I’d bet.

1 Like