JavaScript / JXA Q&A

I wonder if starting a single topic would help here? So any of us with questions could post in this topic, which would keep the clutter down, and also be a good reference source? If not, delete this or do something with it.

But I do have a question (or a hundred). Do any of you build a snippet library? “Library” is probably the wrong term. I mean snippets of code you can paste in when you need to do something, and can’t quite remember how.

And if so, how do you organize it? I was considering using one of the Atom Snippet packages, but none of them are as simple as copying some text and pasting it somewhere else.

Thoughts?


###Summary of Topic
as of 2016-12-23  7:22 PM CT

@DanThomas, with my apologies for editing your main post, I'd like to provide a summary of key posts in this thread.  Please feel free to edit as you see fit.

**Snippet Managers**

* [Mostly in Typinator](https://forum.keyboardmaestro.com/t/javascript-jxa-q-a/4103/2?u=jmichaeltx)	|	2016-06-14	 |	by @ccstone
* [Most in Quiver, key snippets in Typinator](https://forum.keyboardmaestro.com/t/javascript-jxa-q-a/4103/3?u=jmichaeltx)	|	2016-06-14	 |	by @ComplexPoint
* [Quiver is main.  Moving from TextExpander to Typinator](https://forum.keyboardmaestro.com/t/javascript-jxa-q-a/4103/6?u=jmichaeltx)	|	2016-06-14	 |	by @JMichaelTX
* [For "library" types of code](https://forum.keyboardmaestro.com/t/javascript-jxa-q-a/4103/7?u=jmichaeltx)	|	2016-06-14	 |	by @DanThomas
* [Major functions go into an actual Script Library](https://forum.keyboardmaestro.com/t/javascript-jxa-q-a/4103/8?u=jmichaeltx)	|	2016-06-14	 |	by @JMichaelTX
* [Script Libraries (JXA, AppleScript, etc)](https://forum.keyboardmaestro.com/t/javascript-jxa-q-a/4103/11?u=jmichaeltx)	|	2016-06-14	 |	by @JMichaelTX
* 

**Tech References**

* [Conditional (ternary) Operator - JavaScript | MDN](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Conditional_Operator)	|	2016-06-14	 |	by @JMichaelTX
* [typeof (variable name)](https://forum.keyboardmaestro.com/t/javascript-jxa-q-a/4103/18?u=jmichaeltx)	|	2016-06-14	 |	by @ComplexPoint
* [Reduce is JavaScript's version of fold or foldl / foldLeft](https://forum.keyboardmaestro.com/t/javascript-jxa-q-a/4103/21?u=jmichaeltx)	|	2016-06-14	 |	by @ComplexPoint
* [use type signatures to communicate clarity about the intended type behaviour of a function](https://forum.keyboardmaestro.com/t/javascript-jxa-q-a/4103/26?u=jmichaeltx)	|	2016-06-14	 |	by @ComplexPoint
* [Using "find" allows me to exit the iteration loops immediately](https://forum.keyboardmaestro.com/t/javascript-jxa-q-a/4103/30?u=jmichaeltx)	|	2016-06-15	 |	by @DanThomas
* [top level function named run (lower case) has a special status](https://forum.keyboardmaestro.com/t/javascript-jxa-q-a/4103/41?u=jmichaeltx)	|	2016-06-15	 |	by @ComplexPoint
* [How Do I Get "name of me" in JXA?](https://forum.keyboardmaestro.com/t/javascript-jxa-q-a/4103/53?u=jmichaeltx)	|	2016-06-17	 |	by @JMichaelTX
* 


**Tutorials**

* [Video -- How to Debug JavaScript for Automation (JXA) Scripts with Safari Debugger ](https://forum.keyboardmaestro.com/t/javascript-jxa-q-a/4103/57?u=jmichaeltx)	|	2016-06-20	 |	by @JMichaelTX
* [[JXA] Debugging JXA Scripts with Safari Debugger](https://forum.keyboardmaestro.com/t/javascript-jxa-q-a/4103/68?u=jmichaeltx)	|	2016-06-20	 |	by @JMichaelTX
*
1 Like

Hey Dan,

I have most of my stuff in Typinator.

It has sets, global and set-specific smart-search, and many other goodies.

I use clippings in BBEdit for things where its templating system is useful, and I do the same in Script Debugger (although more text-expansions than clippings in SD).

With Typinator if I don't remember exactly what I'm looking for I can pop-up QuickSearch.
Type “sh” to bring up my shell script set.
Then type character sequences or words that are completed with smart-search.

In this case I've brought up my sed and gsed snippets, and I could filter further quite easily.

I have several actual code-snippet managers, but I don't particularly like any of them due to various deficiencies.

-Chris

1 Like

I also have a number of key snippets in Typinator,
and a larger, more general, collection in Quiver http://happenapps.com/#quiver

So you use Typinator for quickly inserting some favorites, and Quiver (great name) to hold your overall collection?

1 Like

Exactly. Some favorites and some semi-boilerplate:

(function() {
    'use strict';

    

})();
function (x) { return x; }
(function () {
    'use strict';

	// TASKPAPER CONTEXT

    function TaskPaperContext(editor, options) {

        return editor
            .selection
            .selectedItems
            .map(function (item) {
                return item.bodyString;
            });
    }
	
	// JAVASCRIPT FOR AUTOMATION CONTEXT

    var tp3 = Application('com.hogbaysoftware.TaskPaper3'),
    		ds = tp3.documents,
		d = ds.length ? ds[0] : (
			ds.push(tp3.Document()), ds[0]
		);

    return d.evaluate({
        script: TaskPaperContext.toString(),
        withOptions: {}
    });

})();

etc etc

1 Like

Good idea. I like it.

I started a similar topic a while back, but it has grown so large it is hard to wade through:
Learning & Using AppleScript & JavaScript for Automation (JXA)

I do much the same, except right now I'm still using TextExpander. I have purchased Typinator and will be moving to it when I have time. I'll upload my TE snippets later today.

I also use KM macros for some of the more complex snippets, that provide many options and have more intelligence.

I really like Quiver, and have a KM macro to capture snippets from anywhere to Quiver. Give me a little time to clean it up, and I'll upload it.

1 Like

So, I’m starting a collection like the following, and I’d like any comments before I get too far:

(function Run() {
    'use strict';
    ObjC.import('AppKit');

    function arrayToPlist() {
        return ObjC.deepUnwrap(
            $.NSPropertyListSerialization.propertyListWithDataOptionsFormatError(
                $(this).dataUsingEncoding($.NSUTF8StringEncoding), 0, 0, null)
        );
    };

    function writePlistFile(jsObject, strPath) {
        $(jsObject).writeToFileAtomically($(strPath).stringByStandardizingPath, true);
    }

    function readPlistFile(strPath) {
        var strFullPath = $(strPath).stringByStandardizingPath;
        return
            ObjC.deepUnwrap($.NSDictionary.dictionaryWithContentsOfFile(strFullPath)) ||
            ObjC.deepUnwrap($.NSArray.arrayWithContentsOfFile(strFullPath));
    }

    function clipboardContainsType(typeString) {
        return ObjC.deepUnwrap($.NSPasteboard.generalPasteboard.pasteboardItems.js[0].types)
            .includes(typeString);
    }

    function getKMActionsFromClipboard() {
        if (!clipboardContainsType("com.stairways.keyboardmaestro.actionarray"))
            return undefined;
        return ObjC.unwrap($.NSPasteboard.generalPasteboard.stringForType('com.stairways.keyboardmaestro.actionarray'));
    }

    function getKMMacrosFromClipboard() {
        if (!clipboardContainsType("com.stairways.keyboardmaestro.macrosarray"))
            return undefined;
        return ObjC.unwrap($.NSPasteboard.generalPasteboard.stringForType('com.stairways.keyboardmaestro.macrosarray'));
    }

    function getKMPlistFromClipboard() {
        if (clipboardContainsType("com.stairways.keyboardmaestro.actionarray"))
            return ObjC.unwrap($.NSPasteboard.generalPasteboard.stringForType('com.stairways.keyboardmaestro.actionarray'));
        if (clipboardContainsType("com.stairways.keyboardmaestro.macrosarray"))
            return ObjC.unwrap($.NSPasteboard.generalPasteboard.stringForType('com.stairways.keyboardmaestro.macrosarray'));
        return undefined;
    }
})()

NOTES:

  1. For “library” types of code, I prefer to have longer lines like these, because they take up less vertical space, and I consider them black boxes, so I don’t really need readability. So I don’t care if you don’t like that. :stuck_out_tongue:

  2. I’m not crazy about the repeated code in the KM Clipboard routines, but I can see a need for each of those functions, so I’ll live with it.

1 Like

I don't put stuff like this in snippets. They all go into an actual Script Library.

We each have our own style, but I prefer clarity over compactness.
In a Script Library, I rarely need to see the actual function code, so vertical space is not at issue. I want clarity and comments in my Lib code, so when I need to debug or update later it is easy to read/understand.

What I do put into snippets (TextExpander, Typinator), is calls to the functions in the Lib.

I get that, and I agree. Except that I’ll be including these in KM scripts, so I don’t want to include a library. I saw your discussion about libraries previously, and this is just my preference - I just want to include the script in the macro.

So in this case I prefer vertical compactness.

These are very good and useful functions – many thanks for sharing them.

Don’t know if you like ternary operators – they appeal to me because they form expressions with values (rather than statements without values), and thus compose well.

In my snippet collection, for example, your getKMMacrosFromClipboard(),
will probably become, out of habit, in a sense:

function getKMMacrosFromClipboard() {
    return clipboardContainsType("com.stairways.keyboardmaestro.macrosarray") ? (
        ObjC.unwrap($.NSPasteboard.generalPasteboard
            .stringForType('com.stairways.keyboardmaestro.macrosarray'))
    ) : undefined;
}
1 Like

As you wish. I'm sure you know that if you build a bunch of KM Macros with embedded scripts, if you find a bug in one of your "library" functions, then you will have to update all of the KM Macros with that function.

Here's my case for using Script Libraries:

  • The user incurs an one-time pain of having to separately download and put the library in the proper folder
  • After that, all other KM Macros that use functions in the same library require no additional effort on the part of the user.
  • When a bug is found, one update to the script library fixes all KM Macros that use it.

In case other readers are wondering, all script libraries (JXA, AppleScript, etc) can go in:
~/Library/Script Libraries

If I was a seasoned JS developer with an existing library of time-tested routines, to which I would probably add new routines infrequently, then a library would make sense for me. But I’m not, and I don’t, so I won’t. At least, for now.

I do like ternary operators, when they make the code more readable. What you’ve written isn’t more readable, for me, right now. But I’m already used to having opening braces at the end of a line - something I never thought I’d get used to, so you never know…

1 Like

I do like them -- sometimes :wink:

For those that would like more info, see:
Conditional (ternary) Operator - JavaScript | MDN

1 Like

When I get content off the clipboard like this:

var app = Application.currentApplication();
app.includeStandardAdditions = true;
var s = app.theClipboard();

Let’s say I’m running the above code, and I want to see what “type” it returned - undefined, an empty string, some other type… How can I tell? I’m talking about eyeballing the result, not actual JS code to test it. If I just “return” it, that doesn’t really tell me, unless it happens to be a string. In C# I would look at it in the debugger, but when I use the Safari debugger, it doesn’t really tell me. Or does it and I just don’t know what I’m seeing?

However, I’d also like to know what shorthand code i can use to say “is this a non-empty string?”

Thanks.

ObjC.import('AppKit');

ObjC.deepUnwrap(
    $.NSPasteboard.generalPasteboard.pasteboardItems.js[0].types
)

Yeah, I know that one. My question was more generic. I should have asked “how can I tell what type any variable is?” I just don’t know whether to check for null, or undefined, or something else…

mixed:

typeof (variable name) returns a string

But a lot of things are Objects, so then you may want to do specific things like:

If (x instanceof Array) {
}

if (x !== undefined) {
}

1 Like

The below posts were moved from:

Hey Rob - Just came across this, and it looks really interesting. A few questions:

  1. You have a “Caution” comment in the beginning. Since you’re only reading from the plist file, is there really a possibility of screwing anything up? If you’d rather not state the answer, you could do it in a PM - I understand liability concerns. I just want to know what the potential issues are, if I do the same thing.

  2. I’m trying to wrap my head around “.reduce”, and I feel like I’m close. Is it possible you’re using .reduce as more of a “foreach”? It kind of looks that way, since you do some ".push"s in it. If not, can you offer more insight?

  3. Is there a reason you have a separate “nameSort” function, instead of just including it as an inline function when you call “.sort”? I ask because for everything else, you don’t use separate functions - you inline them (what’s the right terms for the distinction I’m making?).

Thanks.