Object.Event

An Event Model for JavaScript Objects

Introduction

Object.Event allows you to create and control events on any JavaScript object. Object.Event accomplishes this by providing an observer/observable implementation nearly identical to the one implemented in Prototype's Element.observe(), except that it is not specific to DOM events or Element objects. All Control Suite scripts already use Object.Event for their own event handling, but this solution is not specific to widgets and can be applied to a wide variety of problems.

In Action

You've clicked the ratings controls below 0 times. These ratings have a total value of 0.

I don't have an instance observer.
Neither do I!
But I have an instance observer, and I haven't been rated yet.
var rating_one = new Control.Rating('rating_one',{multiple:true});
var rating_two = new Control.Rating('rating_two',{multiple:true});
var rating_three = new Control.Rating('rating_three',{multiple:true});
//instance observer
rating_three.observe('afterChange',function(new_value){
    $('rating_three_result').update('have been rated ' + new_value);
});
//class observer
var number_of_times_rated = 0;
var total_container = $('rating_total');
var times_container = $('rating_times');
Control.Rating.observe('afterChange',function(rating_instance,new_value){
    ++number_of_times_rated;
    times_container.update(number_of_times_rated + ' time' + (number_of_times_rated == 1 ? '' : 's'));
    total_container.update(parseInt(total_container.innerHTML) + new_value);
});

The function registered via Control.Rating.observe() will be called whenever any rating's value changes. Note that the instance that triggered the event is always prepended to the argument list when registering a class observer. The function registered via rating_three.observe() will only be called when the third rating changes.

When afterChange is fired...

The diagram below shows what happened when a Control.Rating instance fires an afterChange event. Note that the function registered by observe() is called, not observe() itself.

Triggering Events & $break

All events are triggered via the notify() method. Any arguments passed into this function will be passed to all observers. This method will return an array of the collected return values from all observers. notify() is meant to be called inside of the class or object being observed (the instance is going to notify() observers when there is an event, you are not going to notify() the object of an event). To further clarify the matter, if JavaScript had private methods, notify() would be one.

When observing an event on a DOM object, we use Event.stop() to stop event execution / bubbling. Since there is no event object to stop, we throw the special $break variable inside an event handler to accomplish the same thing. If any event handler throws $break, the notify() method will return false instead of an array of collected return values. Control.Modal has beforeOpen and beforeClose events that will look for this. If $break is thrown, the action will be prevented (in this case, open()).

Click to Open Modal Allow modal window to open?

new Control.Modal('modal_test',{
    beforeOpen: function(){
        if(!$('modal_checkbox').checked)
            throw $break;
    }
});

Object Options

If an object has an options property (every Control Suite object does) that contains a callable function with the same name as an event triggered with notify(), it will be treated just like an instance observer. So the falling code is equivalent.

var rating_one = new Control.Rating('rating_one',{
    afterChange: function(new_value){...}	
});
var rating_two = new Control.Rating('rating_two');
rating_two.observe('afterChange',function(new_value){...});

Fallback when Object.Event is not Available

When declaring your class, you may want to call notify() for your event handling wether or not Object.Event is available. This method can act as a stand in. Calling Object.Event.extend() on your class will overwrite this method. observe() and stopObserving() cannot be called on these classes unless Object.Event is available.


...
notify: function(event_name){
    try{
        if(this.options[event_name])
            return [this.options[event_name].apply(this.options[event_name],$A(arguments).slice(1))];
    }catch(e){
        if(e != $break)
            throw e;
        else
            return false;
    }
}
...

Class Methods

Object.Event has only one method, Object.Event.extend(), which gives the object, and it's prototype Object.Event methods.

ReturnNameDescription
voidextend(object)Extend the given object with Object.Event methods.

Inherited Methods

The methods in this table become part of the object that you extend. Note that by design notify() is designed to be implemented inside the class or object. For instance, a Control.Modal instance will notify it's observers of an 'afterChange' event. You would not notify the instance that it had changed.

ReturnNameDescription
voidobserve(string event_name, function observer)Register a given observer.
voidstopObserving(string event_name, function observer)Remove a given observer.
mixednotify(string event_name, [...args])Notify all observers of a given event. The return value will be boolean false if the event was stopped with $break by any observers. Otherwise it will be an array of the responses.

RSS Changelog

Is available at http://livepipe.net/projects/object_event/changelog.rss

Subversion Repository

The Object Event subversion repository is available at: svn://livepipe.net/object_event/

Other Resources

Have you written a post or article about Object.Event? Please contact me!