Skip to content

Instantly share code, notes, and snippets.

@atg
Created August 13, 2012 23:32
Show Gist options
  • Save atg/3344823 to your computer and use it in GitHub Desktop.
Save atg/3344823 to your computer and use it in GitHub Desktop.

Extending Chocolat with Mixins

Chocolat can be extended plugins called mixins. Mixins are written in JavaScript and run on node.js.

Here's what we'll be making today, kids.

Hooks.addMenuItem("Go/Search in Wikipedia", "cmd-ctrl-w", function() {
    Recipe.run(function(r) {
        var wordrange = r.wordRangeForRange(r.selection);
        var word = r.textInRange(wordrange);
        
        var win = new Popover(Editor.current(), wordrange);
        win.size = { width: 320, height: 480 };
        win.url = 'http://en.m.wikipedia.org/w/index.php?search='
                    + encodeURIComponent(word)
                    + '&title=Special%3ASearch';
        win.run();
    });
});

My First Mixin

Chocolat searches three places for mixins:

  1. ~/.chocolat/mixins
  2. ~/Library/Application Support/Chocolat/Mixins
  3. Chocolat.app/Contents/SharedSupport/mixins

We'll be writing a mixin that looks up a word on wikipedia and displays the article, like the OS X dictionary popover.

mkdir -p ~/.chocolat/mixins/wikipedia.chocmixin
choc ~/.chocolat/mixins/wikipedia.chocmixin/init.js

First, add a "Search on Wikipedia" menu item to the Go menu, and do something when the user clicks it

Hooks.addMenuItem("Go/Search on Wikipedia", "cmd-ctrl-w", function () {
    Alert.show("Hello World!");
});

Save that and like magic, a "Search on Wikipedia" menu item will appear in the Go menu! Click it.

Making the Mixin Do Something Useful

OK, so we want to look up the current word on Wikipedia. For that, we need to find the current word. How?

Any task involving text manipulation can be done using a Recipe. This is our own internal API translated to JavaScript so it has all the gizmos that we've accumulated over the years.

Each Recipe starts out with a call to Recipe.run()

Recipe.run(function(r) {
    // do something with r
});

The r object is the instance of the Recipe class. Why a complicated callback and not var r = new Recipe();? Because recipes are atomic. You can cancel your changes at any time by returning false from the callback.

To find the current range, call .wordRangeForRange() on the recipe and pass it the current selection. Chocolat uses Cocoa's idea of a "selection", a text cursor is just a selection of length 0.

Recipe.run(function(r) {
    var wordrange = r.wordRangeForRange(r.selection);
});

Getting the string for that range is another method on the recipe.

Recipe.run(function(r) {
    var wordrange = r.wordRangeForRange(r.selection);
    var word = r.textInRange(wordrange);
});

All together now:

Hooks.addMenuItem("Go/Search on Wikipedia", "cmd-ctrl-w", function () {
    Recipe.run(function(r) {
        var wordrange = r.wordRangeForRange(r.selection);
        var word = r.textInRange(wordrange);
        Alert.show("Your nearest word is '" + word + "'!");
    });
});

Since our scripting language is JavaScript, it's only fitting that our UIs are written in HTML.

Think about that... you can display any UI in Chocolat that you can construct using HTML, CSS and JS. Cool!

For the purposes of this tutorial, we just want to show a page from wikipedia.org.

// Create a popover attached to the current editor
// and positioned over the word range we found earlier
var win = new Popover(Editor.current(), wordrange);

// Set it to the size of an iPhone screen
win.size = { width: 320, height: 480 };

// Do a search on m.wikipedia.org
win.url = 'http://en.m.wikipedia.org/w/index.php?search='
            + encodeURIComponent(word)
            + '&title=Special%3ASearch';

// Show the popover
win.run();

Aaaand we're done. Save and try looking something up.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment