EventHandler = Class.create();

/**
* A general-purpose event handler class that makes it possible for custom objects
* to fire events just like DOM objects
*/
EventHandler.prototype = {
    listeners : {},
    target : null,

    /**
    * Creates a new event listener
    *
    * @param Object target the custom object to which this event handler
    *               is being added
    */
    initialize : function(target) {
        this.target = target;
        this.addMethods();
    },
    
    /**
    * Adds event-handling methods to the custom object
    */
    addMethods : function() {
        this.target.fire = this.fire.bind(this);
        this.target.observe = this.observe.bind(this);
        this.target.observeOnce = this.observeOnce.bind(this);
        this.target.clearEvents = this.clearEvents.bind(this);
    },
    
    
    /**
    * Fires an event and dispatches it to all listeners
    *
    * @param String event the name of the event to fire
    */
    fire : function(event) {
        if (this.listeners[event]) {
            var listenerArguments = $A(arguments).slice(1);
            $A(this.listeners[event]).each(
                function(listener) { 
                    listener.apply(null, listenerArguments);
                }
            );
        }
    },    
    
    /**
    * Adds an event listener to the custom object
    *
    * @param String     event the name of the event being "observed"
    * @param Function   the listener function
    * @param Array      the additional arguments to be passed to the listener
    */
    observe : function(event, listener, listenerArguments) {
        if (this.listeners[event] === undefined) {
            this.listeners[event] = [];            
        }
        
        this.listeners[event].push(
            this.createListener(listener, listenerArguments)
        );
    },
    
    
    /**
    * Similar to observe(), except that the listener is immediately 
    * removed
    *
    * @param String   event               the name of the event
    * @param Function listener            the listener function
    * @param Array    listenerArguments   the additional arguments to be passed to the listener
    */ 
    observeOnce : function (event, listener, listenerArguments) {
        if (this.listeners[event] === undefined) {
            this.listeners[event] = [];
        }
        
        var listener = this.createListener(listener, listenerArguments);
        
        var eventListeners = this.listeners;        
        var container = {};
        
        var wrapper = function() {
            listener.apply(null, arguments);
            eventListeners[event] = $A(eventListeners[event]).without(container.wrapper);
        };
        container.wrapper = wrapper;        
        this.listeners[event].push(wrapper);
    },
    
    /**
    * Removes all event listeners on this objects
    */
    clearEvents : function() {
        this.listeners = {};
    },
    
    /**
    * Creates a new listener method
    *
    * @param Function listener              the original listener method
    * @param Array    listenerArguments     the additional arguments to be passed to the processor
    */
    createListener : function(listener, listenerArguments) {
        if (listenerArguments === undefined) {
            listenerArguments = [];
        }
        var target = this.target;
        return function() {
            listener.apply(
                target,
                arguments
            );
        };
    }
    
}

new EventHandler(window);

Event.observe(window, 'load', function() {window.fire('load')});

