Kris Wallsmith

Symfony Guru at opensky.com.
Discussing web development, Symfony and fatherhood.

Posts tagged mootools

Jul 14

MooTools: Bubbling Controllers

This one’s an oldie but goodie I just pulled out of my tome of a ~/Sites directory. Similar to Aaron Newton’s concept of Events Arbiters, this class fires events for one object on another object, but I’ve applied the bubbling pattern you may be familiar with from native DOM events. Instead of bubbling up a hierarchy of elements, we just bubble up a hierarchy of controllers.

Here’s the class:

Events.BubblesTo = new Class({

  Extends: Events,

  bubblesTo: function(parent, namespace) {
    this.$bubblerParent = parent;
    this.$bubblerNamespace = namespace;
    return this;
  },

  fireEvent: function(type, args, delay) {
    this.parent(type, args, delay);

    if (this.$bubblerParent &&
      'function' == $type(this.$bubblerParent.fireEvent)) {
      if (this.$bubblerNamespace && !type.contains(':'))
        type = this.$bubblerNamespace+':'+type;
      this.$bubblerParent.fireEvent(type, [this].concat(args));
    }

    return this;
  }

});

Here’s a sample implementation:

var Dealer = new Class({
  Implements: Events,
  initialize: function(name){
    this.name = name;
    this.addEvents({
      'deck:shuffle': function(deck){
        alert(this.name+' shuffled deck '+deck.nb);
      },
      'card:flip': function(card, deck){
        alert(this.name+' flipped a '+card.name+' from deck '+deck.nb);
      }
    });
  }
});

var DeckOfCards = new Class({
  Implements: Events.BubblesTo,
  initialize: function(dealer){
    this.nb = ++DeckOfCards.counter;
    this.bubblesTo(dealer, 'deck');
  },
  shuffle: function(){
    this.fireEvent('shuffle', this);
  }
});
DeckOfCards.counter = 0;

var PlayingCard = new Class({
  Implements: Events.BubblesTo,
  initialize: function(deck){
    this.bubblesTo(deck, 'card');
  },
  flip: function(){
    this.fireEvent('flip', this);
  }
});

var dealer = new Dealer('John');
var deck = new DeckOfCards(dealer);
var cards = ['king', 'queen', 'jack'].map(function(name){
  return new PlayingCard(deck);
});

deck.shuffle();
// --> alerts "John shuffled deck 1"

cards.getRandom().flip();
// --> alerts "John just flipped a ___ from deck 1"

Notice the events are both fired from one controller and listened to on another controller. With Events.BubblesTo in place, the Dealer class can easily keep a watchful eye over its decks and cards without having to listen to each one individually.

Sorry for the lack of syntax highlighting, BTW. Anyone have a good way to accomplish PHP and Javascript syntax highlighting on Tumblr?


Jul 12

MooTools: Ignoring the next click

I’m a big fan of MooTools, as I recently tweeted. I typically try to extend this framework as little as possible, since that can be rabbit hole for me, but this method is just too handy to pass by.

The use case I’m working with is distinguishing between a drag/drop interaction and a click interaction on the same element. If you’re dragging an element, the click event will be fired when you drop it, but you may not want to execute the typical click behavior.

Native.implement([Element, Window, Document], {
  ignoreNext: function(type){
    var element = this, events = element.retrieve('events', {});
    var functions = events[type] || { keys: [], values: [] };
    element.removeEvents(type).addEvent(type, function(event){
      event.stop();
      element.removeEvents(type);
      functions.keys.each(function(fn){ element.addEvent(type, fn) });
    });
  }
});

I have this implemented in a drag start handler like so:

element.addEvent('click', function(){ alert('click') }).makeDraggable({
  onStart: function(drag){ drag.ignoreNext('click') },
  onDrop: function(){ alert('drop') }
});

Assuming the Moo team doesn’t change how native events are handled, this gives you a nice reusable method for handling this otherwise brainfart-inducing interaction.