EventPublisher = Class.create();
EventPublisher.prototype = { 
 /**
  * @constructor
  */
 initialize: function() {
  //private variables
  var allEvents = {}; // cache object
  
  //public instance methods
  /**
   * Attaches a {handler} function to the publisher's {eventName} event for execution upon the event firing
   * @param {String} eventName
   * @param {Function} handler
   * @param {Boolean} asynchFlag [optional] Defaults to false if omitted. Indicates whether to execute {handler} asynchronously (true) or not (false).
   */ 
  this.attachEventHandler = function(eventName, handler) {
   eventName = eventName + "_evt"; // appending _evt to event name to avoid collisions
   // using an event cache array to track all handlers for proper cleanup
   if (allEvents[eventName] == null)
    allEvents[eventName] = [];   
   //create a custom object containing the handler method and the asynch flag
   var asynchVar = arguments.length > 2 ? arguments[2] : false;
   var handlerObj = {
    method: handler,
    asynch: asynchVar
   };  
   allEvents[eventName].push(handlerObj);
  };
  
  /**
   * Removes a single handler from a specific event
   * @param {String} eventName The event name to clear the handler from
   * @param {Function} handler A reference to the handler function to un-register from the event
   */ 
  this.removeEventHandler = function(eventName, handler) {
   eventName = eventName + "_evt"; // appending _evt to event name to avoid collisions
   if (allEvents[eventName] != null)
    allEvents[eventName] = allEvents[eventName].reject(function(obj) { return obj.method == handler; });
  };
  
  /**
   * Removes all handlers from a single event
   * @param {String} eventName The event name to clear handlers from
   */ 
  this.clearEventHandlers = function(eventName) {
   eventName = eventName + "_evt"; // appending _evt to event name to avoid collisions
   allEvents[eventName] = null;
  };
  
  /**
   * Removes all handlers from ALL events
   */ 
  this.clearAllEventHandlers = function() {
   allEvents = {};
  };
  
  /**
   * Fires the event {eventName}, resulting in all registered handlers to be executed.
   * @param {String} eventName The name of the event to fire
   * @params {Object} args [optional] Any object, will be passed into the handler function as the only argument
   */
  this.fireEvent = function(eventName) {
   var evtName = eventName + "_evt"; // appending _evt to event name to avoid collisions
   if (allEvents[evtName] != null) {
    var len = allEvents[evtName].length; //optimization       
    for (var i = 0; i < len; i++) {
     try {
      if (arguments.length > 1) {
       if (allEvents[evtName][i].asynch) {
        var eventArgs = arguments[1];
        var method = allEvents[evtName][i].method.bind(this);
        setTimeout(function() { method(eventArgs) }.bind(this), 10);
       }
       else
        allEvents[evtName][i].method(arguments[1]);                        
      } else {
       if (allEvents[evtName][i].asynch) {
        var eventHandler = allEvents[evtName][i].method;
        setTimeout(eventHandler, 1);
       }
       else if (allEvents && allEvents[evtName] && allEvents[evtName][i] && allEvents[evtName][i].method)
        allEvents[evtName][i].method();
      }
     } catch (e) {
      if (this.id)
      {
       alert("error: error in " + this.id + ".fireEvent():\n\nevent name: " + eventName + "\n\nerror message: " + e.message);
      }
      else 
       alert("error: error in [unknown object].fireEvent():\n\nevent name: " + eventName + "\n\nerror message: " + e.message);
     }
    }
   }
  };
 }
};