Ian Oxley

Notes on Web Development and other Miscellany

Toggling JavaScript Indentation in Emacs

I think I’ve written some elisp that is in danger of actually being useful. Its used to toggle the number of spaces used to indent JavaScript code betwee 2 and 4. Why? Well, when I clone or fork JavaScript projects on GitHub, some use 2 spaces for tabs whereas others use 4. Using this function I can toggle between the two settings with ease.

Here’s the code:

(defun js2-toggle-indent ()
  (interactive)
  (setq js-indent-level (if (= js-indent-level 2) 4 2))
  (setq js2-indent-level (if (= js-indent-level 2) 4 2))
  (setq js2-basic-offset (if (= js-indent-level 2) 4 2))
  (message "js-indent-level, js2-indent-level, and js2-basic-offset set to %d"
           js2-basic-offset))

Because I’m using js2-mode I’m toggling js2-indent-level and js2-basic-offset in addition to js-indent-level.

I had tried implementing this by putting js-indent-level, js2-indent-level, and js2-basic-offset in a list and using mapc to modify them. I was trying something like this:

(mapc (lambda (elem)
        (setq elem (if (= (symbol-value elem) 2) 4 2)))
      '(js-indent-level js2-indent-level js2-basic-offset))

But, as far as I could tell, elisp’s dynamic scoping of variables meant this only modified each item in the scope of the lambda function I passed to mapc e.g. outside the scope of this function js2-indent-level remained unchanged. At least I think that is what was happening. If you know different, please let me know.

Timing Is Everything

I had a problem with some Jasmine tests the other day that used the Jasmine Mock Clock. The code under test displayed a countdown timer, and the tests checked that the minutes and seconds counted down after each second, and handled the transition from 2 minutes to 1 minute 59 seconds, etc. correctly. Every time the tests ran, the first test that used the Jasmine Mock Clock would fail, but the rest would pass.

As it turned out, the cause of this was initialising the Jasmine Mock Clock one line too late.

The code under test was a jQuery UI widget, which used setInterval internally to update it’s display. I used beforeEach to setup my tests, create the widget instance, and initialse the Jasmine Mock Clock:

// Before: test was failing
var widget;
...
beforeEach(function() {
    ...
    widget = $('#foo').countdown();
    jasmine.Clock.useMock();
    ...
});

The line widget = $('#foo').countdown() instantiates the countdown widget, and it begins to countdown straight away. But its using window.setInterval internally as jasmine.Clock.useMock() doens’t get called until the next line. By swapping these two lines around, the Jasmine Mock Clock is always used, and all the tests pass.

// After: all tests pass
var widget;
...
beforeEach(function() {
    ...
    jasmine.Clock.useMock();
    widget = $('#foo').countdown();
    ...
});

Book Review: Getting Started With D3 by Mike Dewar (O’Reilly Media)

D3 is a powerful JavaScript library for creating HTML, CSS, and SVG visualisations. Getting Started With D3 provides a good introduction to the library, and guides the reader through creating charts using real data from the New York Metropolitan Transit Authority.

The book starts off by helping you get your environment setup so you can get the most out of the book, from downloading D3 and creating a basic HTML template used throughout the examples, to setting up a basic web server using Python.

Subsequent chapters help the reader build up their knowledge of D3 by working through a series of examples. The first example focuses on using selections, and uses data from the data set to create a HTML and CSS-based bar chart. Next, the book helps you create a scatter graph, and a line chart, using SVG. It then covers making your visualisations more interactive by showing how you can add mouseover effects, and animations. The final set of examples gives you a brief glimpse of the range of possibilities D3 can open up for you by discussing force layout, histogram layout, and stack layout.

I enjoyed reading Getting Started with D3. As an introduction to using D3, it’s very good. Each chapter builds on what has been discussed before, and the examples are easy to follow along with. If you’ve always wanted to get to grips with D3, but were daunted by it’s steep learning curve, this book could be just what you’re looking for.

Getting Started with D3

Disclaimer: This book was reviewed as part of the O’Reilly Blogger Review program.

class(Name) Dismissed

Back in the day, if you need to add, change, or remove a class on a HTML element, you’ll have had little choice but to use the className property:

el.className = 'foo';

if (el.className === 'foo') {
    el.className = 'bar';
}

http://jsfiddle.net/ianoxley/YFfud/

That was easy enough if an element had only one class applied to it, but things got trickier if you had multiple classes to contend with:

var allClasses = el.className.split(/\s/);
for (var i = 0, length = allClasses.length; i < length; i++) {
    if (allClasses[i] === 'foo') {
        allClasses[i] = 'bar';
        break;
    }
}

el.className = allClasses.join(' ');

http://jsfiddle.net/ianoxley/79TKR/

Along came a slew of JavaScript libraries in the mid-2000’s, with convenient methods for adding and removing classes:

// Using jQuery
var el = $('#element_id');
el.addClass('foo');

el.toggleClass('bar');

if (el.hasClass('foo')) {
    el.removeClass('foo');
}

http://jsfiddle.net/ianoxley/HnVCV/

Now, in 2013, the latest version of each browser, with the exception of Opera Mini, all have native support for adding, removing, toggling, and checking if an element has a class applied, via the classList.

classList returns a token list (specifically a DOMTokenList) of all the classes applied to an element. It exposes a length property, and four functions:

  • add – to add a class to the classList
  • remove – to remove a class from the classList
  • toggle – to add a class to the classList, or remove it if it’s already there
  • contains – to check if a class is in the classList

classList Example

<header id="main">...</header>
...
<script>
    var mainHeader = document.getElementById('main');
    if (mainHeader.classList.length === 0) {
        mainHeader.classList.add('foo');
    }

    mainHeader.classList.toggle('bar');

    if (mainHeader.classList.contains('foo')) {
        mainHeader.classList.remove('foo');
    }
</script>

http://jsfiddle.net/ianoxley/FVjgq/

One of the benefits of using the native classList API is performance. I tested the code from the above examples and the native code was 83% faster than the jQuery equivalent:

jsperf.com results

Polyfills exist for older browsers that don’t support classList natively, so if you need to support older browsers you’ll have to judge whether they are a good fit for you, whether to write your own, or whether to fall back on your preferred JavaScript library.

Using Dropbox as a Git Remote

If you’re working on a pet project and aren’t quite ready to share it with the world via GitHub, here’s a handy way you can backup your work to the cloud using Dropbox as a git remote:

  1. Create a new, empty folder in your Dropbox to house your project’s remote:
1
$ mkdir /path/to/Dropbox/gitremotes/yourproject
  1. Initialise a bare git repository in this newly created folder:
1
$ git init --bare /path/to/Dropbox/gitremotes/yourproject

NB A bare repository in Git is one that only contains the revision info and system files (the files you’d normally find in the hidden .git directory), and doesn’t contain the working tree. From Git version 1.7.0 and onwards, a repository has to be a bare repository in order to accept a push, so we must create our Dropbox remote as a bare repository.

  1. Add our new remote to our project:
1
$ git remote add dropbox /path/to/Dropbox/gitremotes/yourproject
  1. Push our work to our dropbox remote:
1
$ git push dropbox master

You can then interact with your Dropbox remote the same way you would with any other.

Node-encdec

A while back I wrote a gist for base58 encoding / decoding in Python, then ported it to JavaScript. The other day I thought it would be good fun to convert the JavaScript version to a npm module so it can run on Node.js, and here’s the result: https://npmjs.org/package/encdec

It’s essentially the same as the original version. The only real difference is the unit tests are now in nodeunit instead of QUnit.

You can install node-encdec via npm:

npm install node-encdec

# or to install globally

  npm install -g node-encdec