BindAll Class Mutator for Saving Your Sanity

MooTools is just the best JavaScript thing ever. And I say that totally subjectively because I honestly don’t have much experience with anything else other than jQuery. Some of the design decisions are nothing short of genius — custom events, element properties, and class mutators — to name a few.

The Binds mutator is particularly headache relieving. My only issue with it is that you need to list out all the methods you want bound, which somehow, I am very prone to jacking up due to forgetfulness or quick refactoring.

Binds : ['update', 'include', ...]  // yadi yada

So since I have nothing better to do in the airport than watch a depressing House episode, I figured I’d write my own BindAll mutator to save myself from my own misery (and maybe save you from yours).

Class.Mutators.BindAll = function(self, bool) {
  if (!bool) return self;      // mutate or get out
	
  var init = self.initialize;  // save original, if there is one
  var exclude = arguments.callee.exclude;

  self.initialize = function() {
    for (var method in this) {
      if (typeof this[method] != 'function' || exclude.contains(method))
        continue;
			
      var unbound = this[method];
      this[method] = unbound.bind(this);
      this[method].__parent = unbound.__parent;
    }
    // if there was an original initialize, run it
    return init ? init.apply(this, arguments) : this;
  };
	
  return self;
};

Class.Mutators.BindAll.exclude = ['constructor', 'initialize', 'parent'];

With this, all methods get bound, and it’s this simple:

BindAll : true  // or anything else truthy

So now, class methods can be passed around without fear of them forgetting who this is:

$('flashing_red').addEvent('click', self.destruct);

This might not be for everyone, but for me, I can’t see why I wouldn’t want all my methods to be bound properly, and if I do run into that scenario, then I’d fall back to the regular Binds mutator. I also made the function name exclude list accessible to the programmer to add more if needed. Bugfixes will be made on the GitHub Gist.

Another class mutator that has gotten a fair bit of my attention is Privates, which has had many implementations, including Nathan White’s (deeply flawed), Thomas Dullnig’s (flawed), and Sean McCarthur’s (not flawed, but not pretty either). I’ve totally embarrassed myself in an email to Aaron Newton over the matter, essentially arguing the inclusion of Nathan White’s implementation into More before realizing that it was creating a bunch of global variables. Soon, I’ll write about my own implementation that I just made, as soon as I quadruple check that it isn’t flawed as well. That one will need specs!

Safari 4 Beta Breaks Class.implement()

I just spent a few hours tracking down this lovely bug that broke my portfolio page in Safari 4 Beta (build 5528.16). If you add a property to a constructor’s prototype after initializing an object from that constructor, then that new property will not be enumerable in a for-in loop (presumably it’s been mistakenly marked DontEnum).

(update: The May 12th update to Safari 4 – build 5528.17 – fixes this issue, woohoo)

I first discovered this when the oneEvent method that I added to the Events Class was not getting implemented into my other Classes. The JS.Class blog mentioned a Function.prototype enumeration bug that may be related, but other than that I couldn’t find any mention of this problem on Google.

Here is the reduction that I submitted to Apple:

// constructor
var House = function() {};
House.prototype = {
  garage : 1,
  yard : 1
};

// this works
var house = new House;
for (var prop in house)
  print('house has a ' + prop);

// add a new property after using constructor
House.prototype.pool = 1;

// we can see the pool in the prototype
for (var prop in House.prototype)
  print('blueprint contains ' + prop);

// enumeration won't find the pool
var mansion = new House;
for (var prop in mansion)
  print('mansion has a ' + prop);

// but the pool is there
print('check for pool in house...' + !!house.pool);
print('check for pool in mansion...' + !!mansion.pool);

function print(line) { document.write(line + '<br/>'); }

Which in Safari 4 Beta, yields…

house has a garage
house has a yard
pool now added to blueprint
blueprint contains garage
blueprint contains yard
blueprint contains pool
mansion has a garage
mansion has a yard
check for pool in house...true
check for pool in mansion...true

The line, mansion has a pool, is never printed in Safari 4 Beta! It does, however, get printed in the latest Webkit Nightly, but I’m not sure when this bug was fixed and if Apple has merged that fix into their Safari 4 branch. I hope they have already, and if not, that they do so when they read my bug report.

I hope this helped put somebody’s mind at ease.

Element Creation Shortcut in MooTools

Lately, I’ve been bothered about the amount of typing and lines required to create a simple element in MooTools.

var curly = new Element('div', {
  id : 'curly',
  'class' : 'stooge'
});

In jQuery, you’re given the convenience of writing HTML into the jQuery function —

var $curly = $('<div id="curly" class="stooge" />');

To me, this is quick and easy, but it’s very unnatural typing HTML into JavaScript and I’ve seen this convenience become quickly (and horrifyingly) abused.1 MooTools deserves something cleaner and cooler, so today I realized I could use a limited selector to create an element. I liked that idea very much.

var curly = $E('div#curly.stooge');

(update: Apparently I’m not the first to think of this. A couple weeks before I wrote this post, this commit was made to MooTools 2.0 adding this capability to new Element. The idea came from Thomas Aylott. I am so happy to see this become a part of the next version of MooTools!)

I know, $E was used in MooTools 1.1 for finding an element by a selector with an optional filter, but that is longer the case in MooTools 1.2. Besides, $A and $H are used for to create a new array or hash, respectively, so it seems only natural to me to use $E for creating an element. If you don’t like my reasoning, then you can change the name and I promise I won’t hunt you down.

function $E(tag, props) {
  if (typeof props == 'string')
    props = { style : props };
  if (typeof tag == 'string') {
    var id = tag.match(/#([\w-]+)/);
    var classes = tag.match(/(?:\.[\w-]+)+/);
    tag = tag.replace(/[#.].*/, '');
    props = props || {};
    if (id) props.id = id[1];
    if (classes) props['class'] = classes[0].replace(/\./g, ' ');
  }
  return new Element(tag || 'div', props);
};

(edit: a style string can now optionally be passed into props, further updates will be on GitHub here)

With this new magical utility function, you can quickly and easily create elements with an optional id and any number of classes, along with additional properties passed into the optional second argument. In fact, you can omit a tag and it’ll default to a <div>.

var moe = $E('#moe.stooge.leader', {...});

I’m pretty excited about using this new shortcut, and I hope you are too. Please shout out in the comments if you find it useful or have any tweaks.

Note to plugin developers: If you decide to use this, please place it inside of a closure so it doesn’t override someone else’s $E function. I saw this suggestion on the MooTools Twitter2 so I can imagine this becoming a problem —

$E = document.getElement.bind(document);

1 Some even prefer to use double quotes and then escape all the attribute quotes. Ughh!

2 My last post on custom events was linked there!

Fun with Custom Events on Elements in MooTools

Tonight, I’ve been playing with the fantastic MooTools plugin, ReMooz by Harald Kirschner, with the hope of adapting it for my upcoming web portfolio. One of the pieces of functionality I was looking for was to close the zoom box when the user clicks outside of it. The solution I thought had the most Moo was to create a custom event1, clickout.

(update: I just discovered Jan Kassens blogged a similar custom event, outerClick. We took very different approaches, but I thought I’d give proper credit)

Element.Events.clickout = {
  base : 'click',  // attach click event to element
  condition : function(event) {
    event.stopPropagation();  // stop event from bubbling up
    return false;  // never run handler when clicking on element
  },
  onAdd : function(fn) {
    this.getDocument().addEvent('click', fn);
  },
  onRemove : function(fn) {
    this.getDocument().removeEvent('click', fn);
  }
};

(edit: fixed bug that prevented links from working inside the element, further updates will be made on GitHub here)

The condition of the custom event is executed each time the base event is fired to with a return value that determines if the handler should be run. The condition above prevents the click event from bubbling2 up to the body, where the handler will actually be attached in the onAdd callback. This effectively runs the handler passed into the clickout event when anywhere on the page is clicked except inside the element where the event was attached. For example, in ReMooz I wanted to add this event to the zoom box when it is opened.

ReMooz.assign('.thumb', {
  ...
  onOpen : function() {
    this.box.addEvent('clickout', this.close.bind(this));
  },
  onClose : function() {
    this.box.removeEvents('clickout');
  }
});

The event must be removed when the zoom box is closed so the click event attached to be body is also removed. If the zoom box didn’t have a close button in it, then I could have applied a singular event3 pattern like the jQuery.one() method, recreated in MooTools below:

Native.implement([Element, Window, Document, Events], {
  oneEvent : function(type, fn) {
    return this.addEvent(type, function() {
      this.removeEvent(type, arguments.callee);
      return fn.apply(this, arguments);
    });
  }
});

(edit: thanks to gonchuki for the idea of using arguments.callee, further updates will be made on GitHub here)

This allows you to attach an event that only will be run once and then automatically detached. The only problem with this method oneEvent() is that its implementation doesn’t allow you to remove a specific handler with the removeEvent() method; you must use removeEvents() instead to remove all events of a certain type the type you added with this method. jQuery doesn’t have this problem because it uses an event proxy, so you can still unbind a specific handler. With a little refactoring, MooTools could allow the same kind of functionality, though the real-world usage of this pattern is admittedly rare.

I deeply apologize for becoming so serious in this post, I’ll try to be funnier next time!

1 Check out the super cool demo and sample code for more info on custom events.

2 A quick refresher course on event bubbling, if desired.

3 I just made that term up. I hope it sticks.

Lazy List Comprehensions in JavaScript, a Lazy Evaluation

A while back I made Proggit a regular stop on my daily Internet stroll. I was struck by the readers’ apparent bias toward Haskell and thought, What the Hell, I should at least attempt to learn something new today. It was here that I had that lazy evaluation Eureka moment. So of course, I then thought to myself, How can I get a piece of that sweet functional programming action in JavaScript?

For the uninitiated, lazy evaluation is, in a nutshell, the concept of a program not computing the value of something until absolutely needed. This is especially useful for long (perhaps even infinite) lists that would cause severe nausea or swelling to compute ahead of time. The example I read about used the Fibonacci sequence to butter up my brain, so that is what I will use here to electrify yours.

Quite simply, I wanted the constructor of my Lazy List to take a definition function, such as the following for the Fibonacci sequence:

function fibs(i) {
  return i <= 1 ? i : fibs(i - 1) + fibs(i - 2);
}

But why would anyone need anything beyond the fibs function written above? If the unlikely programmer who needs the tenth member of the Fib sequence, just ran fibs(10), she would happily get 55 as the answer. Ahh, but she may want the eleventh member shortly thereafter…

Yes, indeed, she needs a simple caching mechanism. This is a recursive definition we’re talking about here, not exactly cheap labor. So here is my first attempt at giving that poor unlikely programmer just what she needs:

// my lazy constructor
function Lazy(def) {
  var cache = [];

  return function(i) {
    return (i in cache) ? cache[i] :
      (cache[i] = def.call(arguments.callee, i));
  };
}

// a more awesome Fibonacci sequence
var fibs = new Lazy(function(i) {
  return i <= 1 ? i : this(i - 1) + this(i - 2);
});

There, now she must be happy. When she wants that eleventh number in the sequence, the ninth and tenth will already be cached and ready to be added together to give 89 as the result. One little goodie I snuck in there was binding the definition function to the generator so we can conveniently call it with the keyword, this. Also, using the new operator is optional, though I find it more canonical to use since we are creating a new generator when calling the constructor.

When pushing the limits of our cool new sequence, our happy but unlikely programmer will eventually hit a brick wall of “too much recursion”, though how soon that occurs very much depends on the browser she is using. Hopefully, when fed up with our simple solution, she’ll fire up Firefox 3 to enjoy its native generator expressions in JavaScript 1.8]

Array-like methods, such as slice and forEach, can be added to our generator, but that will be left as an exercise for the reader (unless you pay me).

1 For more of the bleeding edge, read John Resig’s rundown of JavaScript 1.8 and Mozilla’s coverage of JavaScript 1.7

About Me

I'm Scott Kyle, a MooTools developer and Apple addict. You can follow me on Twitter, fork me on GitHub, and check out some of my work on my portfolio.